/** * Korrektur Archiv API - NiBiS Zentralabitur Documents and Stats * * Split from api.ts to stay under 500 LOC. */ import { getApiBase, apiFetch, getFairnessAnalysis, getKlausuren } from './api-core' // ============================================================================ // ARCHIV API (NiBiS Zentralabitur Documents) // ============================================================================ export interface ArchivDokument { id: string title: string subject: string niveau: string year: number task_number?: number doc_type: string variant?: string bundesland: string minio_path?: string preview_url?: string } export interface ArchivSearchResponse { total: number documents: ArchivDokument[] filters: { subjects: string[] years: number[] niveaus: string[] doc_types: string[] } } export interface ArchivFilters { subject?: string year?: number bundesland?: string niveau?: string doc_type?: string search?: string limit?: number offset?: number } export async function getArchivDocuments(filters: ArchivFilters = {}): Promise { const params = new URLSearchParams() if (filters.subject && filters.subject !== 'Alle') params.append('subject', filters.subject) if (filters.year) params.append('year', filters.year.toString()) if (filters.bundesland && filters.bundesland !== 'Alle') params.append('bundesland', filters.bundesland) if (filters.niveau && filters.niveau !== 'Alle') params.append('niveau', filters.niveau) if (filters.doc_type && filters.doc_type !== 'Alle') params.append('doc_type', filters.doc_type) if (filters.search) params.append('search', filters.search) if (filters.limit) params.append('limit', filters.limit.toString()) if (filters.offset) params.append('offset', filters.offset.toString()) const queryString = params.toString() return apiFetch(`/api/v1/archiv${queryString ? `?${queryString}` : ''}`) } export async function getArchivDocument(docId: string): Promise { return apiFetch(`/api/v1/archiv/${docId}`) } export async function getArchivDocumentUrl(docId: string, expires: number = 3600): Promise<{ url: string; expires_in: number; filename: string }> { return apiFetch<{ url: string; expires_in: number; filename: string }>(`/api/v1/archiv/${docId}/url?expires=${expires}`) } export async function searchArchivSemantic( query: string, options: { year?: number; subject?: string; niveau?: string; limit?: number } = {} ): Promise> { const params = new URLSearchParams({ query }) if (options.year) params.append('year', options.year.toString()) if (options.subject) params.append('subject', options.subject) if (options.niveau) params.append('niveau', options.niveau) if (options.limit) params.append('limit', options.limit.toString()) return apiFetch(`/api/v1/archiv/search/semantic?${params.toString()}`) } export async function getArchivSuggestions(query: string): Promise> { return apiFetch>(`/api/v1/archiv/suggest?query=${encodeURIComponent(query)}`) } export async function getArchivStats(): Promise<{ total_documents: number; total_chunks: number; by_year: Record; by_subject: Record; by_niveau: Record; }> { return apiFetch('/api/v1/archiv/stats') } // ============================================================================ // STATS API (for Dashboard) // ============================================================================ export interface KorrekturStats { totalKlausuren: number totalStudents: number openCorrections: number completedThisWeek: number averageGrade: number timeSavedHours: number } export async function getKorrekturStats(): Promise { try { const klausuren = await getKlausuren() let totalStudents = 0, openCorrections = 0, completedCount = 0, gradeSum = 0, gradedCount = 0 for (const klausur of klausuren) { totalStudents += klausur.student_count || 0 completedCount += klausur.completed_count || 0 openCorrections += (klausur.student_count || 0) - (klausur.completed_count || 0) } for (const klausur of klausuren) { if (klausur.status === 'completed' || klausur.status === 'in_progress') { try { const fairness = await getFairnessAnalysis(klausur.id) if (fairness.average_grade > 0) { gradeSum += fairness.average_grade; gradedCount++ } } catch { /* Skip */ } } } const averageGrade = gradedCount > 0 ? gradeSum / gradedCount : 0 return { totalKlausuren: klausuren.length, totalStudents, openCorrections, completedThisWeek: completedCount, averageGrade: Math.round(averageGrade * 10) / 10, timeSavedHours: Math.round(completedCount * 0.5), } } catch { return { totalKlausuren: 0, totalStudents: 0, openCorrections: 0, completedThisWeek: 0, averageGrade: 0, timeSavedHours: 0 } } }