Add LandSAR cluster worker deployment
This commit is contained in:
@@ -126,6 +126,17 @@ _STATS_CACHE_DATA: Optional[Dict[str, Any]] = None
|
||||
_STATS_CACHE_EXPIRES_AT = 0.0
|
||||
_STATS_CACHE_GENERATED_AT_UTC: Optional[str] = None
|
||||
|
||||
DASHBOARD_STATS_CACHE_TTL_SECONDS = read_int_env(
|
||||
"DASHBOARD_STATS_CACHE_TTL_SECONDS",
|
||||
STATS_CACHE_TTL_SECONDS,
|
||||
minimum=0,
|
||||
maximum=3600,
|
||||
)
|
||||
_DASHBOARD_STATS_CACHE_LOCK = asyncio.Lock()
|
||||
_DASHBOARD_STATS_CACHE_DATA: Optional[Dict[str, Any]] = None
|
||||
_DASHBOARD_STATS_CACHE_EXPIRES_AT = 0.0
|
||||
_DASHBOARD_STATS_CACHE_GENERATED_AT_UTC: Optional[str] = None
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# AOI token store
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@@ -42,6 +42,12 @@ LANDSAR_PRODUCTION_JOB_MAX_ATTEMPTS = read_int_env(
|
||||
minimum=1,
|
||||
maximum=10,
|
||||
)
|
||||
LANDSAR_CLUSTER_ITEM_JOB_MAX_ATTEMPTS = read_int_env(
|
||||
"LANDSAR_CLUSTER_ITEM_JOB_MAX_ATTEMPTS",
|
||||
1,
|
||||
minimum=1,
|
||||
maximum=10,
|
||||
)
|
||||
|
||||
|
||||
class RunJobRequest(BaseModel):
|
||||
@@ -441,6 +447,114 @@ async def submit_run(
|
||||
}
|
||||
|
||||
|
||||
@router.post("/landsar-cluster/run")
|
||||
async def submit_landsar_cluster_run(
|
||||
req: RunJobRequest,
|
||||
current_user: AuthUserORM = Depends(_get_current_user),
|
||||
):
|
||||
if str(req.engine_code or "").strip().lower() != "landsar":
|
||||
raise HTTPException(status_code=400, detail="LandSAR cluster only accepts engine_code='landsar'.")
|
||||
|
||||
registry = _get_registry()
|
||||
engine = registry.get_engine("landsar")
|
||||
if not engine:
|
||||
raise HTTPException(status_code=400, detail="Engine 'landsar' not found.")
|
||||
|
||||
valid_profiles = {profile.code for profile in engine.get_profiles()}
|
||||
if req.profile not in valid_profiles:
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=(
|
||||
f"Engine 'landsar' does not support profile '{req.profile}'. "
|
||||
f"Available profiles: {sorted(valid_profiles)}"
|
||||
),
|
||||
)
|
||||
|
||||
validation_summary = None
|
||||
if hasattr(engine, "validate_root_dir"):
|
||||
try:
|
||||
validation_summary = await asyncio.to_thread(
|
||||
engine.validate_root_dir,
|
||||
req.root_dir,
|
||||
req.num_to_process,
|
||||
req.rerun_mode,
|
||||
)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
if int(validation_summary.get("task_count", 0) or 0) <= 0:
|
||||
if (
|
||||
req.rerun_mode == "unfinished_only"
|
||||
and int(validation_summary.get("skipped_completed_count", 0) or 0) > 0
|
||||
):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail=(
|
||||
f"All discovered Task_* directories already have completed "
|
||||
f"landsar/{req.profile} results under: {req.root_dir}"
|
||||
),
|
||||
)
|
||||
raise HTTPException(status_code=400, detail="No valid LandSAR task directories selected.")
|
||||
|
||||
normalized_extra = dict(req.extra or {})
|
||||
if hasattr(engine, "normalize_extra"):
|
||||
try:
|
||||
normalized_extra = engine.normalize_extra(normalized_extra)
|
||||
except ValueError as exc:
|
||||
raise HTTPException(status_code=400, detail=str(exc)) from exc
|
||||
|
||||
if validation_summary is not None:
|
||||
validated_task_count = validation_summary.get("task_count", 0)
|
||||
normalized_extra.update(
|
||||
{
|
||||
"__validated_task_count": validated_task_count,
|
||||
"__validated_mode": validation_summary.get("mode", ""),
|
||||
"__rerun_mode": req.rerun_mode,
|
||||
"__discovered_task_count": int(validation_summary.get("discovered_task_count", validated_task_count) or 0),
|
||||
"__skipped_completed_count": int(validation_summary.get("skipped_completed_count", 0) or 0),
|
||||
"__cluster": True,
|
||||
}
|
||||
)
|
||||
|
||||
effective_timeout_seconds = req.timeout_seconds
|
||||
if effective_timeout_seconds is None:
|
||||
engine_default_timeout = getattr(engine, "default_timeout_seconds", None)
|
||||
if engine_default_timeout:
|
||||
effective_timeout_seconds = int(engine_default_timeout)
|
||||
|
||||
try:
|
||||
async with _new_session() as db:
|
||||
result = await dinsar_production_service.create_landsar_cluster_run(
|
||||
profile_code=req.profile,
|
||||
root_dir=req.root_dir,
|
||||
num_to_process=req.num_to_process,
|
||||
rerun_mode=req.rerun_mode,
|
||||
timeout_seconds=effective_timeout_seconds,
|
||||
extra=normalized_extra,
|
||||
created_by=getattr(current_user, "username", None),
|
||||
max_attempts=LANDSAR_CLUSTER_ITEM_JOB_MAX_ATTEMPTS,
|
||||
db=db,
|
||||
)
|
||||
except ValueError as exc:
|
||||
message = str(exc)
|
||||
status_code = 409 if "浠诲姟鍐茬獊" in message else 400
|
||||
raise HTTPException(status_code=status_code, detail=message) from exc
|
||||
|
||||
return {
|
||||
"task_id": result["task_id"],
|
||||
"job_id": None,
|
||||
"run_id": result["run_id"],
|
||||
"workflow_run_id": None,
|
||||
"job_type": "LANDSAR_CLUSTER_ITEM",
|
||||
"engine_code": "landsar",
|
||||
"profile": req.profile,
|
||||
"selected_task_count": result.get("selected_task_count", 0),
|
||||
"discovered_task_count": result.get("discovered_task_count", result.get("selected_task_count", 0)),
|
||||
"skipped_completed_count": result.get("skipped_completed_count", 0),
|
||||
"rerun_mode": result.get("rerun_mode", req.rerun_mode),
|
||||
"message": "LandSAR cluster items queued.",
|
||||
}
|
||||
|
||||
|
||||
@router.get("/runs")
|
||||
async def list_runs(limit: int = 20, offset: int = 0):
|
||||
async with _new_session() as db:
|
||||
|
||||
@@ -318,10 +318,7 @@ async def _build_radar_preview_cache(
|
||||
geo_error = "invalid_bbox"
|
||||
has_geo_cache = False
|
||||
else:
|
||||
source_corner_mapping = await asyncio.to_thread(
|
||||
data_service.get_radar_source_corner_mapping,
|
||||
record.file_path,
|
||||
)
|
||||
source_corner_mapping = data_service.get_radar_record_corner_mapping(record)
|
||||
ok_geo, geo_error = await asyncio.to_thread(
|
||||
image_service.create_geocorrected_radar_cached_image,
|
||||
preview_source,
|
||||
@@ -408,6 +405,7 @@ async def _get_cached_radar_preview(data_id: int, db: AsyncSession):
|
||||
raw_cache_path, geo_cache_path = _radar_preview_paths(record)
|
||||
if (
|
||||
(record.preview_cache_status or "NONE") == "READY"
|
||||
and (record.preview_cache_version or "") == settings.RADAR_GEO_CACHE_VERSION
|
||||
and record.preview_cache_path
|
||||
and str(record.preview_cache_path).lower().endswith(".webp")
|
||||
and os.path.exists(record.preview_cache_path)
|
||||
@@ -418,7 +416,7 @@ async def _get_cached_radar_preview(data_id: int, db: AsyncSession):
|
||||
headers={"Cache-Control": "public, max-age=31536000"},
|
||||
)
|
||||
|
||||
if os.path.exists(geo_cache_path):
|
||||
if os.path.exists(geo_cache_path) and (record.preview_cache_version or "") == settings.RADAR_GEO_CACHE_VERSION:
|
||||
return FileResponse(
|
||||
geo_cache_path,
|
||||
media_type="image/webp",
|
||||
@@ -523,7 +521,6 @@ async def search_radar_data_endpoint(
|
||||
product_unique_id: Optional[str] = Form(None),
|
||||
orbit_direction: Optional[str] = Form(None),
|
||||
has_orbit_data: Optional[bool] = Form(None),
|
||||
is_envi_processed: Optional[bool] = Form(None),
|
||||
imaging_date_from: Optional[str] = Form(None),
|
||||
imaging_date_to: Optional[str] = Form(None),
|
||||
region_tree_id: Optional[str] = Form(None),
|
||||
@@ -609,8 +606,6 @@ async def search_radar_data_endpoint(
|
||||
filters.append(RadarDataORM.orbit_direction.ilike(f"%{n_orbit_direction}%"))
|
||||
if has_orbit_data is not None:
|
||||
filters.append(RadarDataORM.has_orbit_data == has_orbit_data)
|
||||
if is_envi_processed is not None:
|
||||
filters.append(RadarDataORM.is_envi_processed == is_envi_processed)
|
||||
if n_date_from:
|
||||
filters.append(RadarDataORM.imaging_date >= n_date_from)
|
||||
if n_date_to:
|
||||
|
||||
+1245
-1
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user