from __future__ import annotations import uuid from fastapi import APIRouter, Depends from sqlalchemy.orm import Session from app.db.session import get_db from app.core.errors import AppError from app.models import Dataset, Job from app.schemas import Envelope, JobRead, QaProviderComparisonRequest from app.services.qa_service import QaService from app.services.job_service import JobService from app.services.quality_service import QualityService from app.utils.response import envelope router = APIRouter(prefix="/qa", tags=["qa"]) @router.post("/detections-vs-reference", response_model=Envelope[JobRead]) def compare_candidate_with_reference( payload: QaProviderComparisonRequest, db: Session = Depends(get_db), ) -> dict: candidate_dataset = db.get(Dataset, payload.candidate_dataset_id) if not candidate_dataset: raise AppError(code="DATASET_NOT_FOUND", message="Candidate dataset not found", status_code=404) job = JobService.run_sync_job( db=db, project_id=candidate_dataset.project_id, job_type="qa.compare-candidate-with-reference", parameters=payload.model_dump(mode="json"), input_dataset_id=candidate_dataset.id, operation=lambda: QaService.compare_candidate_with_reference( db=db, project_id=candidate_dataset.project_id, candidate_dataset_id=payload.candidate_dataset_id, reference_dataset_id=payload.reference_dataset_id, iou_threshold=payload.iou_threshold, area_id=payload.area_id, ).model_dump(mode="json"), ) result_json = job.get("result_json") if isinstance(job, dict) else None if isinstance(result_json, dict) and job.get("status") == "success": quality_check = QualityService.persist_quality_check( db=db, project_id=candidate_dataset.project_id, job_id=uuid.UUID(str(job["id"])), candidate_dataset_id=payload.candidate_dataset_id, reference_dataset_id=payload.reference_dataset_id, check_type="candidate_vs_reference", status=str(result_json.get("status", "ok")), score=result_json.get("f1_score"), parameters=payload.model_dump(mode="json"), findings={ "matches": result_json.get("matches"), "false_positives": result_json.get("false_positives"), "false_negatives": result_json.get("false_negatives"), "warnings": result_json.get("warnings", []), "unsupported_geometry": result_json.get("unsupported_geometry", False), "unsupported_geometries": result_json.get("unsupported_geometries", []), "match_evidence": result_json.get("match_evidence", []), "false_positive_evidence": result_json.get("false_positive_evidence", []), "false_negative_evidence": result_json.get("false_negative_evidence", []), }, metrics={ "precision": result_json.get("precision"), "recall": result_json.get("recall"), "f1": result_json.get("f1_score"), "mean_iou": result_json.get("mean_iou"), "false_positive_count": result_json.get("false_positives"), "false_negative_count": result_json.get("false_negatives"), }, ) result_json["quality_check_id"] = str(quality_check.id) job_record = db.get(Job, uuid.UUID(str(job["id"]))) if job_record: job_record.result_json = result_json db.add(job_record) db.commit() return envelope(job)