Complete temporal detection safety gate
This commit is contained in:
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
import json
|
||||
import logging
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
@@ -13,6 +14,7 @@ from sqlalchemy import func
|
||||
|
||||
from app.core.config import Settings, get_settings
|
||||
from app.core.errors import AppError
|
||||
from app.core.request_context import get_request_id
|
||||
from app.models import AnalysisRun, Dataset, Detection, Job, Project, VectorFeature
|
||||
from app.schemas.detection import DetectionListResponse, DetectionRead, DetectionRunListResponse, DetectionRunRead, DetectionRunResponse
|
||||
from app.services.detection_georeferencing import pixel_bbox_to_epsg4326_polygon
|
||||
@@ -21,9 +23,13 @@ from app.services.model_asset_catalog_service import ModelAssetCatalogService
|
||||
from app.services.model_registry_service import ModelRegistryService
|
||||
from app.services.qa_service import QaService
|
||||
from app.services.quality_service import QualityService
|
||||
from app.services.temporal_compatibility_service import TemporalCompatibilityService
|
||||
from app.services.yolo_adapter import YoloDetectionAdapter
|
||||
|
||||
|
||||
logger = logging.getLogger("geointel.detection")
|
||||
|
||||
|
||||
class DetectionService:
|
||||
@staticmethod
|
||||
def _now() -> datetime:
|
||||
@@ -58,6 +64,7 @@ class DetectionService:
|
||||
details={"dataset_type": dataset.dataset_type},
|
||||
status_code=400,
|
||||
)
|
||||
TemporalCompatibilityService.ensure_detection_source_supported(dataset)
|
||||
|
||||
selected_model_asset = None
|
||||
if model_id == resolved_settings.yolo_model_id and model_asset_id:
|
||||
@@ -96,6 +103,15 @@ class DetectionService:
|
||||
}
|
||||
job = DetectionService._create_job(db, project_id, dataset_id, run_parameters)
|
||||
analysis_run = DetectionService._create_analysis_run(db, project_id, dataset_id, job.id, model, run_parameters)
|
||||
logger.info(
|
||||
"detection_started request_id=%s project_id=%s dataset_id=%s job_id=%s analysis_run_id=%s model_id=%s",
|
||||
get_request_id(),
|
||||
project_id,
|
||||
dataset_id,
|
||||
job.id,
|
||||
analysis_run.id,
|
||||
model.model_id,
|
||||
)
|
||||
|
||||
if not model.configured:
|
||||
message = model.limitation_message
|
||||
@@ -281,6 +297,13 @@ class DetectionService:
|
||||
raise AppError(code="INVALID_DATASET_SCOPE", message="Reference dataset does not belong to detection project", status_code=400)
|
||||
if reference_dataset.dataset_type not in {"vector", "geojson"}:
|
||||
raise AppError(code="INVALID_DATASET_TYPE", message="Reference dataset must be vector data", status_code=400)
|
||||
candidate_dataset = db.get(Dataset, run.dataset_id)
|
||||
if not candidate_dataset:
|
||||
raise AppError(code="DATASET_NOT_FOUND", message="Detection source dataset not found", status_code=404)
|
||||
temporal_compatibility = TemporalCompatibilityService.assess_detection_qa(
|
||||
candidate_dataset,
|
||||
reference_dataset,
|
||||
)
|
||||
|
||||
detections = DetectionService._query_detection_rows(
|
||||
db,
|
||||
@@ -421,6 +444,7 @@ class DetectionService:
|
||||
"class_name": class_name,
|
||||
"min_confidence": min_confidence,
|
||||
"coverage_policy": coverage_summary["mode"],
|
||||
"temporal_compatibility": temporal_compatibility,
|
||||
},
|
||||
findings={
|
||||
"matches": evidence.matches,
|
||||
@@ -429,6 +453,7 @@ class DetectionService:
|
||||
"warnings": coverage_warnings + evidence.warnings,
|
||||
"unsupported_geometry": evidence.unsupported,
|
||||
"coverage": coverage_summary,
|
||||
"temporal_compatibility": temporal_compatibility,
|
||||
"box_to_footprint_diagnostics": box_to_footprint_diagnostics,
|
||||
"match_evidence": evidence.match_evidence,
|
||||
"false_positive_evidence": evidence.false_positive_evidence,
|
||||
@@ -443,6 +468,17 @@ class DetectionService:
|
||||
"false_negative_count": evidence.false_negatives,
|
||||
},
|
||||
)
|
||||
logger.info(
|
||||
"detection_qa_completed request_id=%s job_id=%s analysis_run_id=%s quality_check_id=%s "
|
||||
"candidate_dataset_id=%s reference_dataset_id=%s status=%s",
|
||||
get_request_id(),
|
||||
run.job_id,
|
||||
analysis_run_id,
|
||||
quality_check.id,
|
||||
run.dataset_id,
|
||||
reference_dataset_id,
|
||||
status,
|
||||
)
|
||||
return {
|
||||
"status": status,
|
||||
"quality_check_id": str(quality_check.id),
|
||||
@@ -462,6 +498,7 @@ class DetectionService:
|
||||
"iou_threshold": iou_threshold,
|
||||
"warnings": coverage_warnings + evidence.warnings,
|
||||
"coverage": coverage_summary,
|
||||
"temporal_compatibility": temporal_compatibility,
|
||||
"box_to_footprint_diagnostics": box_to_footprint_diagnostics,
|
||||
"match_evidence": evidence.match_evidence,
|
||||
"false_positive_evidence": evidence.false_positive_evidence,
|
||||
|
||||
Reference in New Issue
Block a user