Harden LandSAR cluster handoff

This commit is contained in:
2026-06-26 12:28:47 +08:00
parent fced6f4a7f
commit 40178ad1a4
10 changed files with 418 additions and 101 deletions
+75 -3
View File
@@ -13,6 +13,8 @@ import tempfile
import zipfile
from typing import Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import AsyncSession
from fastapi import (
APIRouter,
Depends,
@@ -24,11 +26,14 @@ from fastapi import (
)
from fastapi.responses import FileResponse
from starlette.background import BackgroundTask
from sqlalchemy.ext.asyncio import AsyncSession
from ..config import settings
from ..database import get_db
from ..models.orm import DinsarProductionRunItemORM
from ..models.orm import (
DinsarProductionExecutionORM,
DinsarProductionRunItemORM,
DinsarProductionRunORM,
)
from ..services.cluster_transport import safe_extract_zip
router = APIRouter()
@@ -263,17 +268,84 @@ async def upload_cluster_result(
# ----- catalog registration ------------------------------------------------
from ..services.result_catalog_service import result_catalog_service as rcs
from ..services.dinsar_production_service import dinsar_production_service
try:
publish_result = await rcs.publish_from_sources(db, [extract_dir])
processed = int(publish_result.get("processed", 0) or 0)
failed = int(publish_result.get("failed", 0) or 0)
if processed > 0:
await rcs.rebuild_catalog(db, full_rebuild=True)
if processed != 1 or failed != 0:
raise RuntimeError(
f"expected processed=1 failed=0, got processed={processed} failed={failed}"
)
details = publish_result.get("details") if isinstance(publish_result, dict) else []
detail = next(
(
item_detail
for item_detail in (details or [])
if str(item_detail.get("run_key") or "").strip() == normalized_run_key
),
(details or [{}])[0] if details else {},
)
execution_manifest_path = str(
detail.get("execution_manifest_path")
or os.path.join(extract_dir, "execution_manifest.json")
)
current_pointer_path = str(detail.get("current_pointer_path") or "")
if not os.path.isfile(execution_manifest_path):
raise RuntimeError(
f"execution manifest was not created: {execution_manifest_path}"
)
if not current_pointer_path or not os.path.isfile(current_pointer_path):
raise RuntimeError(
f"current pointer was not created: {current_pointer_path or '<empty>'}"
)
run = await db.get(DinsarProductionRunORM, str(item.run_id or ""))
if run is None:
raise RuntimeError(f"Cluster run not found for item {item_id}: {item.run_id}")
execution_result = await db.execute(
select(DinsarProductionExecutionORM).where(
DinsarProductionExecutionORM.item_id == item.id,
DinsarProductionExecutionORM.run_key == normalized_run_key,
)
)
execution = execution_result.scalar_one_or_none()
if execution is None:
raise RuntimeError(
f"Cluster execution not found for item={item.id} run_key={normalized_run_key}"
)
execution.output_dir = extract_dir
item.latest_output_dir = extract_dir
await dinsar_production_service.mark_item_completed(
run=run,
item=item,
execution=execution,
manifest_path=execution_manifest_path,
metrics={
"cluster_upload": True,
"catalog_processed": processed,
"catalog_failed": failed,
"current_pointer_path": current_pointer_path,
},
db=db,
)
final_status = await dinsar_production_service.finalize_cluster_run_if_complete(
run,
db=db,
)
return {
"registered": processed > 0,
"completed": True,
"final_status": final_status,
"processed": processed,
"failed": int(publish_result.get("failed", 0) or 0),
"failed": failed,
"catalog_path": extract_dir,
"execution_manifest_path": execution_manifest_path,
"current_pointer_path": current_pointer_path,
}
except Exception as exc:
raise HTTPException(