145 lines
4.8 KiB
TypeScript
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,
|
|
}
|
|
}
|