Files
geointel/frontend/src/hooks/useQualityWorkflow.ts
T
Codex b9674a0e42
GeoIntel CI / docs-smoke (push) Has been cancelled
GeoIntel CI / contract-smoke (push) Has been cancelled
Add QA evidence map overlay
2026-06-25 03:17:49 +02:00

145 lines
4.8 KiB
TypeScript

import { useState } from 'react'
import { qaApi } from '../services/api'
import type { JobRead, QaComparisonRequest, QaComparisonResult, QualityCheckRead, QualityEvidenceGeoJsonResponse } from '../types'
import { formatError } from '../lib/formatError'
interface QualityWorkflowOptions {
selectedProjectId: string | null
loadProjectData: (projectId: string) => Promise<unknown>
}
export function useQualityWorkflow({ selectedProjectId, loadProjectData }: QualityWorkflowOptions) {
const [qaCandidateDatasetId, setQaCandidateDatasetId] = useState('')
const [qaReferenceDatasetId, setQaReferenceDatasetId] = useState('')
const [qaAreaId, setQaAreaId] = useState('')
const [qaIouThreshold, setQaIouThreshold] = useState(0.5)
const [qaRunning, setQaRunning] = useState(false)
const [qaResult, setQaResult] = useState<QaComparisonResult | null>(null)
const [qaError, setQaError] = useState<string | null>(null)
const [qualityChecks, setQualityChecks] = useState<QualityCheckRead[]>([])
const [qualityChecksError, setQualityChecksError] = useState<string | null>(null)
const [qualityEvidenceGeoJson, setQualityEvidenceGeoJson] = useState<QualityEvidenceGeoJsonResponse | null>(null)
const [qualityEvidenceLoading, setQualityEvidenceLoading] = useState(false)
const [qualityEvidenceError, setQualityEvidenceError] = useState<string | null>(null)
const loadQualityChecks = async (projectId = selectedProjectId): Promise<QualityCheckRead[] | void> => {
if (!projectId) {
setQualityChecks([])
return []
}
setQualityChecksError(null)
try {
const response = await qaApi.listQualityChecks(projectId)
setQualityChecks(response.items)
return response.items
} catch (error) {
setQualityChecksError(formatError(error, 'Failed to load QA/QC results'))
}
}
const loadQualityEvidenceGeoJson = async (qualityCheckId: string, projectId = selectedProjectId): Promise<QualityEvidenceGeoJsonResponse | null> => {
if (!projectId) {
setQualityEvidenceError('Select a project first')
return null
}
setQualityEvidenceLoading(true)
setQualityEvidenceError(null)
try {
const response = await qaApi.getQualityEvidenceGeoJson(projectId, qualityCheckId)
setQualityEvidenceGeoJson(response)
return response
} catch (error) {
setQualityEvidenceError(formatError(error, 'Failed to load QA/QC evidence overlay'))
return null
} finally {
setQualityEvidenceLoading(false)
}
}
const clearQualityEvidenceGeoJson = () => {
setQualityEvidenceGeoJson(null)
setQualityEvidenceError(null)
}
const runQaComparison = async () => {
if (!selectedProjectId) {
setQaError('Select a project first')
return
}
if (!qaCandidateDatasetId) {
setQaError('Select candidate dataset')
return
}
if (!qaReferenceDatasetId) {
setQaError('Select reference dataset')
return
}
if (qaCandidateDatasetId === qaReferenceDatasetId) {
setQaError('Candidate and reference datasets must be different')
return
}
if (!Number.isFinite(qaIouThreshold) || qaIouThreshold < 0 || qaIouThreshold > 1) {
setQaError('IoU threshold must be between 0 and 1')
return
}
setQaError(null)
setQaResult(null)
setQaRunning(true)
try {
const request: QaComparisonRequest = {
candidate_dataset_id: qaCandidateDatasetId,
reference_dataset_id: qaReferenceDatasetId,
iou_threshold: qaIouThreshold,
area_id: qaAreaId || null,
}
const job: JobRead = await qaApi.runQa(request)
if (job.status === 'failed') {
setQaError(job.error_message || 'QA comparison failed')
return
}
const payload = job.result_json
if (!payload || typeof payload !== 'object') {
setQaError('QA result was not available')
return
}
const parsed = payload as unknown as QaComparisonResult
if (!parsed || typeof parsed.status !== 'string') {
setQaError('QA result format was unexpected')
return
}
setQaResult(parsed)
await loadQualityChecks(selectedProjectId)
if (job.output_dataset_id) {
await loadProjectData(selectedProjectId)
}
} catch (error) {
setQaError(error instanceof Error ? error.message : 'QA comparison failed')
} finally {
setQaRunning(false)
}
}
return {
qaCandidateDatasetId,
qaReferenceDatasetId,
qaAreaId,
qaIouThreshold,
qaRunning,
qaResult,
qaError,
qualityChecks,
qualityChecksError,
qualityEvidenceGeoJson,
qualityEvidenceLoading,
qualityEvidenceError,
loadQualityChecks,
loadQualityEvidenceGeoJson,
clearQualityEvidenceGeoJson,
runQaComparison,
setQaCandidateDatasetId,
setQaReferenceDatasetId,
setQaAreaId,
setQaIouThreshold,
}
}