backend-lehrer (5 files): - alerts_agent/db/repository.py (992 → 5), abitur_docs_api.py (956 → 3) - teacher_dashboard_api.py (951 → 3), services/pdf_service.py (916 → 3) - mail/mail_db.py (987 → 6) klausur-service (5 files): - legal_templates_ingestion.py (942 → 3), ocr_pipeline_postprocess.py (929 → 4) - ocr_pipeline_words.py (876 → 3), ocr_pipeline_ocr_merge.py (616 → 2) - KorrekturPage.tsx (956 → 6) website (5 pages): - mail (985 → 9), edu-search (958 → 8), mac-mini (950 → 7) - ocr-labeling (946 → 7), audit-workspace (871 → 4) studio-v2 (5 files + 1 deleted): - page.tsx (946 → 5), MessagesContext.tsx (925 → 4) - korrektur (914 → 6), worksheet-cleanup (899 → 6) - useVocabWorksheet.ts (888 → 3) - Deleted dead page-original.tsx (934 LOC) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
114 lines
4.0 KiB
TypeScript
114 lines
4.0 KiB
TypeScript
'use client'
|
|
|
|
import { useState } from 'react'
|
|
import type { OCRSession, OCRStats } from '../types'
|
|
|
|
const API_BASE = process.env.NEXT_PUBLIC_KLAUSUR_SERVICE_URL || 'http://localhost:8086'
|
|
|
|
export default function ExportTab({
|
|
sessions,
|
|
selectedSession,
|
|
setSelectedSession,
|
|
stats,
|
|
onError,
|
|
}: {
|
|
sessions: OCRSession[]
|
|
selectedSession: string | null
|
|
setSelectedSession: (id: string | null) => void
|
|
stats: OCRStats | null
|
|
onError: (msg: string) => void
|
|
}) {
|
|
const [exportFormat, setExportFormat] = useState<'generic' | 'trocr' | 'llama_vision'>('generic')
|
|
const [exporting, setExporting] = useState(false)
|
|
const [exportResult, setExportResult] = useState<any>(null)
|
|
|
|
const handleExport = async () => {
|
|
setExporting(true)
|
|
try {
|
|
const res = await fetch(`${API_BASE}/api/v1/ocr-label/export`, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
export_format: exportFormat,
|
|
session_id: selectedSession,
|
|
}),
|
|
})
|
|
|
|
if (res.ok) {
|
|
const data = await res.json()
|
|
setExportResult(data)
|
|
} else {
|
|
onError('Export fehlgeschlagen')
|
|
}
|
|
} catch (err) {
|
|
onError('Netzwerkfehler')
|
|
} finally {
|
|
setExporting(false)
|
|
}
|
|
}
|
|
|
|
return (
|
|
<div className="space-y-6">
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<h3 className="text-lg font-semibold mb-4">Training-Daten exportieren</h3>
|
|
<div className="space-y-4">
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-1">Export-Format</label>
|
|
<select
|
|
value={exportFormat}
|
|
onChange={(e) => setExportFormat(e.target.value as typeof exportFormat)}
|
|
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500"
|
|
>
|
|
<option value="generic">Generic JSON</option>
|
|
<option value="trocr">TrOCR Fine-Tuning</option>
|
|
<option value="llama_vision">Llama Vision Fine-Tuning</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-1">Session (optional)</label>
|
|
<select
|
|
value={selectedSession || ''}
|
|
onChange={(e) => setSelectedSession(e.target.value || null)}
|
|
className="w-full px-3 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-500"
|
|
>
|
|
<option value="">Alle Sessions</option>
|
|
{sessions.map((session) => (
|
|
<option key={session.id} value={session.id}>{session.name}</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
<button
|
|
onClick={handleExport}
|
|
disabled={exporting || (stats?.exportable_items || 0) === 0}
|
|
className="w-full px-4 py-2 bg-primary-600 text-white rounded-lg hover:bg-primary-700 disabled:opacity-50"
|
|
>
|
|
{exporting ? 'Exportiere...' : `${stats?.exportable_items || 0} Samples exportieren`}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
{exportResult && (
|
|
<div className="bg-white rounded-lg shadow p-6">
|
|
<h3 className="text-lg font-semibold mb-4">Export-Ergebnis</h3>
|
|
<div className="bg-green-50 border border-green-200 rounded-lg p-4 mb-4">
|
|
<p className="text-green-800">
|
|
{exportResult.exported_count} Samples erfolgreich exportiert
|
|
</p>
|
|
<p className="text-sm text-green-600">
|
|
Batch: {exportResult.batch_id}
|
|
</p>
|
|
</div>
|
|
<div className="bg-slate-50 p-4 rounded-lg overflow-auto max-h-64">
|
|
<pre className="text-xs">{JSON.stringify(exportResult.samples?.slice(0, 3), null, 2)}</pre>
|
|
{(exportResult.samples?.length || 0) > 3 && (
|
|
<p className="text-slate-500 mt-2">... und {exportResult.samples.length - 3} weitere</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
)
|
|
}
|