104 lines
2.8 KiB
TypeScript
104 lines
2.8 KiB
TypeScript
/**
|
|
* RPRO3: API-Client für den RennmausPro-III-Backup-Import.
|
|
* POST /import/rpro3/analyze -> multipart 'backup' (+ optional 'images') -> Auswertung (kein Schreiben)
|
|
* POST /import/rpro3/execute -> derselbe Upload -> idempotenter Import
|
|
*
|
|
* Upload via FormData (nicht api.post — das erzwingt application/json); der Browser setzt
|
|
* den multipart-Boundary-Header selbst.
|
|
*/
|
|
import { API_BASE_URL, ApiError } from './client'
|
|
import { de } from '../strings/de'
|
|
|
|
export interface Rpro3Counts {
|
|
ownAnimals: number
|
|
externalRaw: number
|
|
externalAfterDedup: number
|
|
litters: number
|
|
contacts: number
|
|
genotypes: number
|
|
duplicatesMerged: number
|
|
mergeClusters: number
|
|
}
|
|
|
|
export interface Rpro3AmbiguousVariant {
|
|
count: number
|
|
dob: string
|
|
farbe: string
|
|
origin: string
|
|
isOwn: boolean
|
|
}
|
|
|
|
export interface Rpro3AmbiguousName {
|
|
name: string
|
|
variants: Rpro3AmbiguousVariant[]
|
|
bareCount: number
|
|
}
|
|
|
|
export interface Rpro3MergeSample {
|
|
name: string
|
|
recordCount: number
|
|
dob: string
|
|
farbe: string
|
|
origin: string
|
|
}
|
|
|
|
export interface Rpro3AnalyzeResult {
|
|
counts: Rpro3Counts
|
|
newVsCurrentNew: number
|
|
newVsCurrentExisting: number
|
|
topMerges: Rpro3MergeSample[]
|
|
ambiguousNames: Rpro3AmbiguousName[]
|
|
photosProvided: boolean
|
|
photoFilesAvailable: number
|
|
}
|
|
|
|
export interface Rpro3ExecuteResult {
|
|
gerbilsImported: number
|
|
littersImported: number
|
|
contactsImported: number
|
|
healthRecordsImported: number
|
|
weightRecordsImported: number
|
|
photosImported: number
|
|
message: string
|
|
}
|
|
|
|
async function postUpload<T>(path: string, backup: File, images: File | null): Promise<T> {
|
|
const form = new FormData()
|
|
form.append('backup', backup)
|
|
if (images) form.append('images', images)
|
|
|
|
let response: Response
|
|
try {
|
|
response = await fetch(`${API_BASE_URL}${path}`, { method: 'POST', body: form })
|
|
} catch {
|
|
throw new ApiError(de.api.errors.network, null, path)
|
|
}
|
|
if (!response.ok) {
|
|
// 400 trägt eine deutsche Klartext-Fehlermeldung (z. B. „keine _rpro3.db gefunden").
|
|
let body: unknown
|
|
let text: string | null = null
|
|
try {
|
|
text = await response.clone().text()
|
|
body = text ? JSON.parse(text) : undefined
|
|
} catch {
|
|
body = undefined
|
|
}
|
|
const message =
|
|
response.status === 400 && text
|
|
? text.replace(/^"|"$/g, '')
|
|
: response.status >= 500
|
|
? de.api.errors.server
|
|
: de.api.errors.unknown
|
|
throw new ApiError(message, response.status, path, body)
|
|
}
|
|
return (await response.json()) as T
|
|
}
|
|
|
|
export function analyzeRpro3(backup: File, images: File | null): Promise<Rpro3AnalyzeResult> {
|
|
return postUpload<Rpro3AnalyzeResult>('/import/rpro3/analyze', backup, images)
|
|
}
|
|
|
|
export function executeRpro3(backup: File, images: File | null): Promise<Rpro3ExecuteResult> {
|
|
return postUpload<Rpro3ExecuteResult>('/import/rpro3/execute', backup, images)
|
|
}
|