Files
breakpilot-lehrer/studio-v2/lib/korrektur/api-core.ts
Benjamin Admin 451365a312 [split-required] Split remaining 500-680 LOC files (final batch)
website (17 pages + 3 components):
- multiplayer/wizard, middleware/wizard+test-wizard, communication
- builds/wizard, staff-search, voice, sbom/wizard
- foerderantrag, mail/tasks, tools/communication, sbom
- compliance/evidence, uni-crawler, brandbook (already done)
- CollectionsTab, IngestionTab, RiskHeatmap

backend-lehrer (5 files):
- letters_api (641 → 2), certificates_api (636 → 2)
- alerts_agent/db/models (636 → 3)
- llm_gateway/communication_service (614 → 2)
- game/database already done in prior batch

klausur-service (2 files):
- hybrid_vocab_extractor (664 → 2)
- klausur-service/frontend: api.ts (620 → 3), EHUploadWizard (591 → 2)

voice-service (3 files):
- bqas/rag_judge (618 → 3), runner (529 → 2)
- enhanced_task_orchestrator (519 → 2)

studio-v2 (6 files):
- korrektur/[klausurId] (578 → 4), fairness (569 → 2)
- AlertsWizard (552 → 2), OnboardingWizard (513 → 2)
- korrektur/api.ts (506 → 3), geo-lernwelt (501 → 2)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 08:56:45 +02:00

140 lines
6.5 KiB
TypeScript

/**
* Korrektur Core API - Base functions and CRUD operations
*
* Split from api.ts. This module contains the base fetch wrapper
* and all core Klausur/Student/Annotation/Fairness operations.
*/
import type {
Klausur, StudentWork, CriteriaScores, Annotation,
AnnotationPosition, AnnotationType, FairnessAnalysis,
EHSuggestion, GradeInfo, CreateKlausurData,
} from '@/app/korrektur/types'
export const getApiBase = (): string => {
if (typeof window === 'undefined') return 'http://localhost:8086'
const { hostname } = window.location
return hostname === 'localhost' ? 'http://localhost:8086' : '/klausur-api'
}
export async function apiFetch<T>(endpoint: string, options: RequestInit = {}): Promise<T> {
const url = `${getApiBase()}${endpoint}`
const response = await fetch(url, { ...options, headers: { 'Content-Type': 'application/json', ...options.headers } })
if (!response.ok) {
const errorData = await response.json().catch(() => ({ detail: 'Unknown error' }))
throw new Error(errorData.detail || `HTTP ${response.status}`)
}
return response.json()
}
// Klausuren
export async function getKlausuren(): Promise<Klausur[]> {
const data = await apiFetch<{ klausuren: Klausur[] }>('/api/v1/klausuren')
return data.klausuren || []
}
export async function getKlausur(id: string): Promise<Klausur> {
return apiFetch<Klausur>(`/api/v1/klausuren/${id}`)
}
export async function createKlausur(data: CreateKlausurData): Promise<Klausur> {
return apiFetch<Klausur>('/api/v1/klausuren', { method: 'POST', body: JSON.stringify(data) })
}
export async function deleteKlausur(id: string): Promise<void> {
await apiFetch(`/api/v1/klausuren/${id}`, { method: 'DELETE' })
}
// Students
export async function getStudents(klausurId: string): Promise<StudentWork[]> {
const data = await apiFetch<{ students: StudentWork[] }>(`/api/v1/klausuren/${klausurId}/students`)
return data.students || []
}
export async function getStudent(studentId: string): Promise<StudentWork> {
return apiFetch<StudentWork>(`/api/v1/students/${studentId}`)
}
export async function uploadStudentWork(klausurId: string, file: File, anonymId: string): Promise<StudentWork> {
const formData = new FormData()
formData.append('file', file)
formData.append('anonym_id', anonymId)
const response = await fetch(`${getApiBase()}/api/v1/klausuren/${klausurId}/students`, { method: 'POST', body: formData })
if (!response.ok) {
const errorData = await response.json().catch(() => ({ detail: 'Upload failed' }))
throw new Error(errorData.detail || `HTTP ${response.status}`)
}
return response.json()
}
export async function deleteStudent(studentId: string): Promise<void> {
await apiFetch(`/api/v1/students/${studentId}`, { method: 'DELETE' })
}
// Criteria & Gutachten
export async function updateCriteria(studentId: string, criteria: CriteriaScores): Promise<StudentWork> {
return apiFetch<StudentWork>(`/api/v1/students/${studentId}/criteria`, { method: 'PUT', body: JSON.stringify({ criteria_scores: criteria }) })
}
export async function updateGutachten(studentId: string, gutachten: string): Promise<StudentWork> {
return apiFetch<StudentWork>(`/api/v1/students/${studentId}/gutachten`, { method: 'PUT', body: JSON.stringify({ gutachten }) })
}
export async function generateGutachten(studentId: string): Promise<{ gutachten: string }> {
return apiFetch<{ gutachten: string }>(`/api/v1/students/${studentId}/gutachten/generate`, { method: 'POST' })
}
// Annotations
export async function getAnnotations(studentId: string): Promise<Annotation[]> {
const data = await apiFetch<{ annotations: Annotation[] }>(`/api/v1/students/${studentId}/annotations`)
return data.annotations || []
}
export async function createAnnotation(studentId: string, annotation: { page: number; position: AnnotationPosition; type: AnnotationType; text: string; severity?: 'minor' | 'major' | 'critical'; suggestion?: string; linked_criterion?: string }): Promise<Annotation> {
return apiFetch<Annotation>(`/api/v1/students/${studentId}/annotations`, { method: 'POST', body: JSON.stringify(annotation) })
}
export async function updateAnnotation(annotationId: string, updates: Partial<{ text: string; severity: 'minor' | 'major' | 'critical'; suggestion: string }>): Promise<Annotation> {
return apiFetch<Annotation>(`/api/v1/annotations/${annotationId}`, { method: 'PUT', body: JSON.stringify(updates) })
}
export async function deleteAnnotation(annotationId: string): Promise<void> {
await apiFetch(`/api/v1/annotations/${annotationId}`, { method: 'DELETE' })
}
// EH/RAG
export async function getEHSuggestions(studentId: string, criterion?: string): Promise<EHSuggestion[]> {
const data = await apiFetch<{ suggestions: EHSuggestion[] }>(`/api/v1/students/${studentId}/eh-suggestions`, { method: 'POST', body: JSON.stringify({ criterion }) })
return data.suggestions || []
}
export async function queryRAG(query: string, topK: number = 5): Promise<{ results: Array<{ text: string; score: number; metadata: any }> }> {
return apiFetch('/api/v1/eh/rag-query', { method: 'POST', body: JSON.stringify({ query, top_k: topK }) })
}
export async function uploadEH(file: File): Promise<{ id: string; name: string }> {
const formData = new FormData()
formData.append('file', file)
const response = await fetch(`${getApiBase()}/api/v1/eh/upload`, { method: 'POST', body: formData })
if (!response.ok) {
const errorData = await response.json().catch(() => ({ detail: 'EH Upload failed' }))
throw new Error(errorData.detail || `HTTP ${response.status}`)
}
return response.json()
}
// Fairness & Export
export async function getFairnessAnalysis(klausurId: string): Promise<FairnessAnalysis> {
return apiFetch<FairnessAnalysis>(`/api/v1/klausuren/${klausurId}/fairness`)
}
export async function getGradeInfo(): Promise<GradeInfo> {
return apiFetch<GradeInfo>('/api/v1/grade-info')
}
export function getGutachtenExportUrl(studentId: string): string { return `${getApiBase()}/api/v1/students/${studentId}/export/gutachten` }
export function getAnnotationsExportUrl(studentId: string): string { return `${getApiBase()}/api/v1/students/${studentId}/export/annotations` }
export function getOverviewExportUrl(klausurId: string): string { return `${getApiBase()}/api/v1/klausuren/${klausurId}/export/overview` }
export function getAllGutachtenExportUrl(klausurId: string): string { return `${getApiBase()}/api/v1/klausuren/${klausurId}/export/all-gutachten` }
export function getStudentFileUrl(studentId: string): string { return `${getApiBase()}/api/v1/students/${studentId}/file` }