Harden DInSAR and SBAS production edges
This commit is contained in:
@@ -78,6 +78,8 @@ HIGH_RISK_WRITE_PATH_PREFIXES = (
|
|||||||
"/api/scan-data",
|
"/api/scan-data",
|
||||||
"/api/scan-dinsar-results",
|
"/api/scan-dinsar-results",
|
||||||
"/api/dinsar-results/",
|
"/api/dinsar-results/",
|
||||||
|
"/api/dinsar-production",
|
||||||
|
"/api/sbas-insar-production",
|
||||||
"/api/ai/",
|
"/api/ai/",
|
||||||
"/api/idl/launch-workbench",
|
"/api/idl/launch-workbench",
|
||||||
"/api/idl/worker/",
|
"/api/idl/worker/",
|
||||||
|
|||||||
@@ -106,19 +106,6 @@ def _is_postgresql_session(db: AsyncSession) -> bool:
|
|||||||
return (dialect_name or "").lower() == "postgresql"
|
return (dialect_name or "").lower() == "postgresql"
|
||||||
except Exception:
|
except Exception:
|
||||||
return False
|
return False
|
||||||
"""
|
|
||||||
try:
|
|
||||||
scan_dirs = list(request.results_directories) if request else []
|
|
||||||
if not scan_dirs:
|
|
||||||
scan_dirs = _get_default_dinsar_scan_dirs()
|
|
||||||
if not scan_dirs:
|
|
||||||
raise HTTPException(status_code=400, detail="未提供结果目录,且 MONITOR_DINSAR_DIRS 未配置。")
|
|
||||||
bind = db.get_bind()
|
|
||||||
dialect_name = getattr(getattr(bind, "dialect", None), "name", "")
|
|
||||||
return (dialect_name or "").lower() == "postgresql"
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
"""
|
|
||||||
|
|
||||||
|
|
||||||
async def _apply_list_query_statement_timeout(db: AsyncSession) -> None:
|
async def _apply_list_query_statement_timeout(db: AsyncSession) -> None:
|
||||||
@@ -249,6 +236,8 @@ async def scan_dinsar_results_endpoint(
|
|||||||
)
|
)
|
||||||
await db.commit()
|
await db.commit()
|
||||||
return {"message": "D-InSAR 统一扫描任务已进入队列", "task_id": task_id}
|
return {"message": "D-InSAR 统一扫描任务已进入队列", "task_id": task_id}
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
except ValueError as e:
|
except ValueError as e:
|
||||||
raise HTTPException(status_code=409, detail=str(e))
|
raise HTTPException(status_code=409, detail=str(e))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@@ -232,12 +232,6 @@ async def submit_run(
|
|||||||
status_code=400,
|
status_code=400,
|
||||||
detail=f"PyINT 输入资产预检未通过: {detail or '请先修复阻塞项。'}",
|
detail=f"PyINT 输入资产预检未通过: {detail or '请先修复阻塞项。'}",
|
||||||
)
|
)
|
||||||
if not pyint_preview.get("allow_submit"):
|
|
||||||
detail = summarize_preview_blockers(pyint_preview)
|
|
||||||
raise HTTPException(
|
|
||||||
status_code=400,
|
|
||||||
detail=f"PyINT 输入资产预检未通过: {detail or '请先修复阻塞项。'}",
|
|
||||||
)
|
|
||||||
|
|
||||||
payload = {
|
payload = {
|
||||||
"engine_code": req.engine_code,
|
"engine_code": req.engine_code,
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import mimetypes
|
||||||
import os
|
import os
|
||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
@@ -258,4 +259,5 @@ async def get_dinsar_product_preview(
|
|||||||
preview_path = str(detail.get("preview_path") or "").strip()
|
preview_path = str(detail.get("preview_path") or "").strip()
|
||||||
if not preview_path or not os.path.isfile(preview_path):
|
if not preview_path or not os.path.isfile(preview_path):
|
||||||
raise HTTPException(status_code=404, detail="Preview not found")
|
raise HTTPException(status_code=404, detail="Preview not found")
|
||||||
return FileResponse(preview_path, media_type="image/webp")
|
media_type = mimetypes.guess_type(preview_path)[0] or "application/octet-stream"
|
||||||
|
return FileResponse(preview_path, media_type=media_type)
|
||||||
|
|||||||
@@ -794,6 +794,12 @@ class SbasInsarProductionService:
|
|||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
source_paths = self._resolve_source_roots(source_roots)
|
source_paths = self._resolve_source_roots(source_roots)
|
||||||
orbit_paths = self._resolve_orbit_roots(orbit_roots)
|
orbit_paths = self._resolve_orbit_roots(orbit_roots)
|
||||||
|
root_warnings = self._build_root_resolution_warnings(
|
||||||
|
source_roots=source_roots,
|
||||||
|
orbit_roots=orbit_roots,
|
||||||
|
source_paths=source_paths,
|
||||||
|
orbit_paths=orbit_paths,
|
||||||
|
)
|
||||||
normalized_mode = self._normalize_discovery_mode(discovery_mode)
|
normalized_mode = self._normalize_discovery_mode(discovery_mode)
|
||||||
min_aoi_coverage_ratio = max(0.0, min(1.0, float(min_aoi_coverage_ratio or 0.0)))
|
min_aoi_coverage_ratio = max(0.0, min(1.0, float(min_aoi_coverage_ratio or 0.0)))
|
||||||
min_common_overlap_ratio = max(0.0, min(1.0, float(min_common_overlap_ratio or 0.0)))
|
min_common_overlap_ratio = max(0.0, min(1.0, float(min_common_overlap_ratio or 0.0)))
|
||||||
@@ -818,6 +824,8 @@ class SbasInsarProductionService:
|
|||||||
if not force_refresh:
|
if not force_refresh:
|
||||||
cached = self._read_discovery_cache(cache_key)
|
cached = self._read_discovery_cache(cache_key)
|
||||||
if cached is not None:
|
if cached is not None:
|
||||||
|
cached = dict(cached)
|
||||||
|
cached["warnings"] = root_warnings
|
||||||
return cached
|
return cached
|
||||||
|
|
||||||
scenes: list[dict[str, Any]] = []
|
scenes: list[dict[str, Any]] = []
|
||||||
@@ -919,6 +927,7 @@ class SbasInsarProductionService:
|
|||||||
"scene_count": len(scenes),
|
"scene_count": len(scenes),
|
||||||
"candidate_count": len(candidates),
|
"candidate_count": len(candidates),
|
||||||
"errors": errors[:50],
|
"errors": errors[:50],
|
||||||
|
"warnings": root_warnings,
|
||||||
"items": candidates,
|
"items": candidates,
|
||||||
}
|
}
|
||||||
snapshot_path = self._write_runtime_json(
|
snapshot_path = self._write_runtime_json(
|
||||||
@@ -3698,6 +3707,58 @@ class SbasInsarProductionService:
|
|||||||
raw_values = [r"D:\orbit_pools\envi"]
|
raw_values = [r"D:\orbit_pools\envi"]
|
||||||
return self._dedupe_existing_dirs(raw_values)
|
return self._dedupe_existing_dirs(raw_values)
|
||||||
|
|
||||||
|
def _build_root_resolution_warnings(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
source_roots: list[str] | None,
|
||||||
|
orbit_roots: list[str] | None,
|
||||||
|
source_paths: list[Path],
|
||||||
|
orbit_paths: list[Path],
|
||||||
|
) -> list[dict[str, Any]]:
|
||||||
|
warnings: list[dict[str, Any]] = []
|
||||||
|
source_requested = source_roots or self._split_config_paths(settings.GAMMA_SBAS_SOURCE_ROOTS) or [r"D:\LuTan1_Image_Pool"]
|
||||||
|
orbit_requested = orbit_roots or self._split_config_paths(settings.GAMMA_SBAS_ORBIT_ROOTS) or [r"D:\orbit_pools\envi"]
|
||||||
|
|
||||||
|
source_missing = self._missing_root_values(source_requested)
|
||||||
|
orbit_missing = self._missing_root_values(orbit_requested)
|
||||||
|
if source_missing:
|
||||||
|
warnings.append(
|
||||||
|
{
|
||||||
|
"code": "SOURCE_ROOTS_NOT_FOUND",
|
||||||
|
"message": "Some configured SBAS source roots do not exist and were ignored.",
|
||||||
|
"requested_roots": source_requested,
|
||||||
|
"missing_roots": source_missing,
|
||||||
|
"resolved_roots": [str(path) for path in source_paths],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if orbit_missing:
|
||||||
|
warnings.append(
|
||||||
|
{
|
||||||
|
"code": "ORBIT_ROOTS_NOT_FOUND",
|
||||||
|
"message": "Some configured SBAS orbit roots do not exist and were ignored.",
|
||||||
|
"requested_roots": orbit_requested,
|
||||||
|
"missing_roots": orbit_missing,
|
||||||
|
"resolved_roots": [str(path) for path in orbit_paths],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if source_requested and not source_paths:
|
||||||
|
warnings.append(
|
||||||
|
{
|
||||||
|
"code": "NO_VALID_SOURCE_ROOTS",
|
||||||
|
"message": "No valid SBAS source roots were resolved; discovery will return no scenes.",
|
||||||
|
"requested_roots": source_requested,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
if orbit_requested and not orbit_paths:
|
||||||
|
warnings.append(
|
||||||
|
{
|
||||||
|
"code": "NO_VALID_ORBIT_ROOTS",
|
||||||
|
"message": "No valid SBAS orbit roots were resolved; orbit matching will be unavailable.",
|
||||||
|
"requested_roots": orbit_requested,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
return warnings
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _discovery_cache_key(
|
def _discovery_cache_key(
|
||||||
*,
|
*,
|
||||||
@@ -3785,6 +3846,22 @@ class SbasInsarProductionService:
|
|||||||
roots.append(path)
|
roots.append(path)
|
||||||
return roots
|
return roots
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _missing_root_values(values: list[str]) -> list[str]:
|
||||||
|
missing: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for value in values:
|
||||||
|
text = str(value or "").strip().strip('"').strip("'")
|
||||||
|
if not text:
|
||||||
|
continue
|
||||||
|
key = os.path.normcase(text)
|
||||||
|
if key in seen:
|
||||||
|
continue
|
||||||
|
seen.add(key)
|
||||||
|
if not SbasInsarProductionService._existing_path_variants(text):
|
||||||
|
missing.append(text)
|
||||||
|
return missing
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _existing_path_variants(value: str) -> list[Path]:
|
def _existing_path_variants(value: str) -> list[Path]:
|
||||||
text = str(value or "").strip().strip('"').strip("'")
|
text = str(value or "").strip().strip('"').strip("'")
|
||||||
|
|||||||
@@ -2,7 +2,10 @@ import apiClient from './client';
|
|||||||
|
|
||||||
export const getDinsarResults = (params) => apiClient.get('/dinsar-results', { params }).then(r => r.data);
|
export const getDinsarResults = (params) => apiClient.get('/dinsar-results', { params }).then(r => r.data);
|
||||||
export const labelDinsarResult = (resultId, label) =>
|
export const labelDinsarResult = (resultId, label) =>
|
||||||
apiClient.post(`/dinsar-results/${resultId}/label`, { label }).then(r => r.data);
|
apiClient.post(
|
||||||
|
`/dinsar-results/${resultId}/label`,
|
||||||
|
new URLSearchParams(label === null || label === undefined ? {} : { label: String(label) })
|
||||||
|
).then(r => r.data);
|
||||||
export const scanDinsarResults = (params) => apiClient.post('/scan-dinsar-results', params).then(r => r.data);
|
export const scanDinsarResults = (params) => apiClient.post('/scan-dinsar-results', params).then(r => r.data);
|
||||||
export const exportDinsarResults = (resultIds, targetDir) =>
|
export const exportDinsarResults = (resultIds, targetDir) =>
|
||||||
apiClient.post('/dinsar-results/export', { result_ids: resultIds, target_dir: targetDir }).then(r => r.data);
|
apiClient.post('/dinsar-results/export', { result_ids: resultIds, target_dir: targetDir }).then(r => r.data);
|
||||||
|
|||||||
Reference in New Issue
Block a user