chore: initialize insar management system v2
This commit is contained in:
@@ -0,0 +1,103 @@
|
||||
"""
|
||||
models 包入口。
|
||||
重新导出所有 ORM 和 Schema 符号,保持现有 `from .models import ...` 语句零改动。
|
||||
"""
|
||||
from .orm import (
|
||||
RadarDataORM,
|
||||
DinsarResultORM,
|
||||
ResultProductORM,
|
||||
DinsarProductProfileORM,
|
||||
ResultAssetORM,
|
||||
ResultIssueORM,
|
||||
ResultCatalogStateORM,
|
||||
PairingCacheStateORM,
|
||||
PairingDirtySceneORM,
|
||||
PairingMetricCacheORM,
|
||||
PairingNetworkRunORM,
|
||||
PairingNetworkEdgeORM,
|
||||
HazardPointORM,
|
||||
SystemTaskORM,
|
||||
TaskLogORM,
|
||||
SystemJobORM,
|
||||
ScanStateORM,
|
||||
ManagedRootORM,
|
||||
ScanCursorORM,
|
||||
PathInventoryORM,
|
||||
WorkflowDefORM,
|
||||
WorkflowRunORM,
|
||||
WorkflowStepORM,
|
||||
WorkflowArtifactORM,
|
||||
SystemWorkerHeartbeatORM,
|
||||
DinsarTaskBatchORM,
|
||||
DinsarTaskItemORM,
|
||||
PsTaskBatchORM,
|
||||
PsTaskItemORM,
|
||||
PsTimeseriesRunORM,
|
||||
AuthUserORM,
|
||||
AuthSessionORM,
|
||||
AuthAuditLogORM,
|
||||
AuthRateLimitORM,
|
||||
SARSceneGeoORM,
|
||||
FloodDetectionORM,
|
||||
WaterDetectionORM,
|
||||
GF3ProcessingORM,
|
||||
AiDiagnosisORM,
|
||||
)
|
||||
from .schemas import (
|
||||
HazardPoint,
|
||||
DinsarResult,
|
||||
ScanRequest,
|
||||
ManagedRootInfo,
|
||||
ScanCursorInfo,
|
||||
RadarData,
|
||||
RadarDataPage,
|
||||
DinsarResultPage,
|
||||
PairingRequest,
|
||||
RadarPair,
|
||||
PairingResponse,
|
||||
PsRequest,
|
||||
TaskInfo,
|
||||
AuthUserInfo,
|
||||
AuthAuditLogInfo,
|
||||
RadarPreviewStatusInfo,
|
||||
DinsarTaskBatch,
|
||||
DinsarTaskItem,
|
||||
PsTaskBatch,
|
||||
PsTaskItem,
|
||||
PsTimeseriesRun,
|
||||
WaterDetectRequest,
|
||||
WaterDetectResponse,
|
||||
GF3ProcessRequest,
|
||||
GF3ProcessResponse,
|
||||
AiDiagnosisCreate,
|
||||
AiDiagnosisResponse,
|
||||
AiDiagnosisListResponse,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
# ORM
|
||||
"RadarDataORM", "DinsarResultORM", "HazardPointORM",
|
||||
"ResultProductORM", "DinsarProductProfileORM", "ResultAssetORM",
|
||||
"ResultIssueORM", "ResultCatalogStateORM",
|
||||
"PairingCacheStateORM", "PairingDirtySceneORM", "PairingMetricCacheORM",
|
||||
"PairingNetworkRunORM", "PairingNetworkEdgeORM",
|
||||
"SystemTaskORM", "TaskLogORM", "SystemJobORM", "ScanStateORM",
|
||||
"ManagedRootORM", "ScanCursorORM", "PathInventoryORM",
|
||||
"WorkflowDefORM", "WorkflowRunORM", "WorkflowStepORM", "WorkflowArtifactORM",
|
||||
"SystemWorkerHeartbeatORM",
|
||||
"DinsarTaskBatchORM", "DinsarTaskItemORM", "PsTaskBatchORM", "PsTaskItemORM",
|
||||
"PsTimeseriesRunORM",
|
||||
"AuthUserORM", "AuthSessionORM", "AuthAuditLogORM", "AuthRateLimitORM",
|
||||
"SARSceneGeoORM", "FloodDetectionORM",
|
||||
"WaterDetectionORM", "GF3ProcessingORM", "AiDiagnosisORM",
|
||||
# Schemas
|
||||
"HazardPoint", "DinsarResult", "ScanRequest", "ManagedRootInfo", "ScanCursorInfo",
|
||||
"RadarData", "RadarDataPage", "DinsarResultPage",
|
||||
"PairingRequest", "RadarPair", "PairingResponse",
|
||||
"PsRequest", "TaskInfo",
|
||||
"AuthUserInfo", "AuthAuditLogInfo", "RadarPreviewStatusInfo",
|
||||
"DinsarTaskBatch", "DinsarTaskItem", "PsTaskBatch", "PsTaskItem", "PsTimeseriesRun",
|
||||
"WaterDetectRequest", "WaterDetectResponse",
|
||||
"GF3ProcessRequest", "GF3ProcessResponse",
|
||||
"AiDiagnosisCreate", "AiDiagnosisResponse", "AiDiagnosisListResponse",
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,607 @@
|
||||
"""
|
||||
Pydantic Schema 定义。
|
||||
所有 API 请求/响应模型均在此文件中定义。
|
||||
"""
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, computed_field, field_validator
|
||||
|
||||
from ..config import read_int_env
|
||||
|
||||
MAX_SCAN_DIRECTORY_COUNT = read_int_env(
|
||||
"MAX_SCAN_DIRECTORY_COUNT",
|
||||
64,
|
||||
minimum=1,
|
||||
maximum=500,
|
||||
)
|
||||
MAX_SCAN_PATH_LENGTH = read_int_env(
|
||||
"MAX_SCAN_PATH_LENGTH",
|
||||
2048,
|
||||
minimum=64,
|
||||
maximum=32767,
|
||||
)
|
||||
|
||||
|
||||
def _normalize_directory_list(value: Any, field_name: str) -> List[str]:
|
||||
if value is None:
|
||||
return []
|
||||
if not isinstance(value, list):
|
||||
raise ValueError(f"{field_name} must be a list.")
|
||||
|
||||
normalized: List[str] = []
|
||||
for raw in value:
|
||||
path = str(raw or "").strip()
|
||||
if not path:
|
||||
continue
|
||||
if len(path) > MAX_SCAN_PATH_LENGTH:
|
||||
raise ValueError(
|
||||
f"{field_name} contains a path longer than {MAX_SCAN_PATH_LENGTH} characters."
|
||||
)
|
||||
if path not in normalized:
|
||||
normalized.append(path)
|
||||
|
||||
if len(normalized) > MAX_SCAN_DIRECTORY_COUNT:
|
||||
raise ValueError(
|
||||
f"{field_name} exceeds max directory count ({MAX_SCAN_DIRECTORY_COUNT})."
|
||||
)
|
||||
return normalized
|
||||
|
||||
|
||||
class HazardPoint(BaseModel):
|
||||
id: int
|
||||
tybh: str
|
||||
hazard_type: Optional[str] = None
|
||||
hazard_name: Optional[str] = None
|
||||
city: Optional[str] = None
|
||||
county: Optional[str] = None
|
||||
township: Optional[str] = None
|
||||
longitude: float
|
||||
latitude: float
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class DinsarResult(BaseModel):
|
||||
id: int
|
||||
product_id: Optional[str] = None
|
||||
compat_result_id: Optional[int] = None
|
||||
name: str
|
||||
task_name: Optional[str] = None
|
||||
task_alias: Optional[str] = None
|
||||
pair_key: Optional[str] = None
|
||||
pair_uid: Optional[str] = None
|
||||
network_run_id: Optional[str] = None
|
||||
network_edge_id: Optional[int] = None
|
||||
policy_version: Optional[str] = None
|
||||
selection_strategy: Optional[str] = None
|
||||
file_path: str
|
||||
min_lon: float
|
||||
min_lat: float
|
||||
max_lon: float
|
||||
max_lat: float
|
||||
coverage_polygon: Optional[Union[List, dict]] = None
|
||||
is_cached: bool
|
||||
|
||||
ai_score: Optional[float] = None
|
||||
user_label: Optional[int] = None
|
||||
ai_report: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class ScanRequest(BaseModel):
|
||||
"""API 请求体模型,用于触发数据扫描。"""
|
||||
radar_data_directories: List[str] = Field(default_factory=list)
|
||||
orbit_data_directory: Optional[str] = None
|
||||
dinsar_results_directories: List[str] = Field(default_factory=list)
|
||||
|
||||
@field_validator("radar_data_directories", mode="before")
|
||||
@classmethod
|
||||
def _validate_radar_dirs(cls, value: Any) -> List[str]:
|
||||
return _normalize_directory_list(value, "radar_data_directories")
|
||||
|
||||
@field_validator("dinsar_results_directories", mode="before")
|
||||
@classmethod
|
||||
def _validate_dinsar_dirs(cls, value: Any) -> List[str]:
|
||||
return _normalize_directory_list(value, "dinsar_results_directories")
|
||||
|
||||
@field_validator("orbit_data_directory", mode="before")
|
||||
@classmethod
|
||||
def _validate_orbit_dir(cls, value: Any) -> Optional[str]:
|
||||
if value is None:
|
||||
return None
|
||||
normalized = str(value).strip()
|
||||
if not normalized:
|
||||
return None
|
||||
if len(normalized) > MAX_SCAN_PATH_LENGTH:
|
||||
raise ValueError(
|
||||
f"orbit_data_directory is longer than {MAX_SCAN_PATH_LENGTH} characters."
|
||||
)
|
||||
return normalized
|
||||
|
||||
|
||||
class ManagedRootInfo(BaseModel):
|
||||
id: int
|
||||
root_code: str
|
||||
root_role: str
|
||||
display_name: str
|
||||
path: str
|
||||
path_kind: str
|
||||
source_kind: str
|
||||
source_ref: Optional[str] = None
|
||||
scan_mode: str
|
||||
owner_engine: Optional[str] = None
|
||||
enabled: bool
|
||||
exists_flag: bool
|
||||
metadata_json: Optional[Dict[str, Any]] = None
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class ScanCursorInfo(BaseModel):
|
||||
id: int
|
||||
root_ref_id: int
|
||||
cursor_key: str
|
||||
cursor_type: str
|
||||
scan_scope: str
|
||||
status: str
|
||||
last_scan_started_at: Optional[datetime] = None
|
||||
last_scan_finished_at: Optional[datetime] = None
|
||||
last_seen_mtime: Optional[float] = None
|
||||
last_seen_entry_count: Optional[int] = None
|
||||
last_seen_fingerprint: Optional[str] = None
|
||||
last_error: Optional[str] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class RadarData(BaseModel):
|
||||
"""雷达数据的数据模型 (Pydantic),用于 API 输入和输出。"""
|
||||
id: int
|
||||
satellite: str
|
||||
imaging_date: str
|
||||
imaging_mode: str
|
||||
orbit_direction: Optional[str] = None
|
||||
polarization: str
|
||||
satellite_mode: Optional[str] = None
|
||||
receiving_station: Optional[str] = None
|
||||
orbit_circle: Optional[str] = None
|
||||
scene_center_lon: Optional[float] = None
|
||||
scene_center_lat: Optional[float] = None
|
||||
acquisition_time_utc: Optional[str] = None
|
||||
product_type: Optional[str] = None
|
||||
product_level: Optional[str] = None
|
||||
product_unique_id: Optional[str] = None
|
||||
file_path: str
|
||||
has_orbit_data: bool
|
||||
orbit_file_path: Optional[str] = None
|
||||
is_envi_processed: bool = False
|
||||
coverage_polygon: List[Tuple[float, float]]
|
||||
|
||||
min_lon: float
|
||||
min_lat: float
|
||||
max_lon: float
|
||||
max_lat: float
|
||||
preview_cache_status: str = "NONE"
|
||||
preview_cache_version: Optional[str] = None
|
||||
preview_cache_updated_at: Optional[datetime] = None
|
||||
preview_cache_error: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@computed_field
|
||||
@property
|
||||
def coverage_bbox(self) -> Tuple[float, float, float, float]:
|
||||
"""A computed property to provide the bbox tuple, used by existing logic."""
|
||||
return (self.min_lon, self.min_lat, self.max_lon, self.max_lat)
|
||||
|
||||
|
||||
class RadarDataPage(BaseModel):
|
||||
items: List[RadarData]
|
||||
total: int
|
||||
limit: int
|
||||
offset: int
|
||||
has_more: bool
|
||||
|
||||
|
||||
class DinsarResultPage(BaseModel):
|
||||
items: List[DinsarResult]
|
||||
total: int
|
||||
limit: int
|
||||
offset: int
|
||||
has_more: bool
|
||||
|
||||
|
||||
class PairingRequest(BaseModel):
|
||||
"""D-InSAR 配对请求的参数模型(增强版 v2.0)"""
|
||||
# === 时空约束(保留) ===
|
||||
time_baseline_min: int = Field(default=1, ge=0, le=3650)
|
||||
time_baseline_max: int = Field(default=90, ge=1, le=3650)
|
||||
overlap_threshold: float = Field(default=0.5, ge=0.0, le=1.0)
|
||||
spatial_baseline_max_meters: int = Field(default=3000, ge=0, le=100000)
|
||||
coverage_diversity_penalty: float = Field(default=0.3, ge=0.0, le=1.0)
|
||||
require_same_imaging_mode: bool = True
|
||||
require_same_polarization: bool = True
|
||||
aoi_overlap_threshold: Optional[float] = Field(default=None, ge=0.0, le=1.0)
|
||||
|
||||
# === 双池日期(新增) ===
|
||||
master_date_from: Optional[str] = Field(default=None, pattern=r'^\d{8}$|^$')
|
||||
master_date_to: Optional[str] = Field(default=None, pattern=r'^\d{8}$|^$')
|
||||
slave_date_from: Optional[str] = Field(default=None, pattern=r'^\d{8}$|^$')
|
||||
slave_date_to: Optional[str] = Field(default=None, pattern=r'^\d{8}$|^$')
|
||||
|
||||
# === 配对策略(新增) ===
|
||||
strategy: str = Field(default="all", pattern=r'^(all|sbas|sequential|star)$')
|
||||
num_connections: int = Field(default=1, ge=1, le=10)
|
||||
reference_image_id: Optional[int] = None
|
||||
|
||||
# === 多卫星支持(新增) ===
|
||||
allowed_satellites: Optional[List[str]] = None
|
||||
cross_satellite_pairing: bool = False
|
||||
|
||||
# === 向后兼容(保留) ===
|
||||
start_date: Optional[str] = Field(default=None, pattern=r'^\d{8}$|^$')
|
||||
|
||||
@field_validator(
|
||||
'master_date_from',
|
||||
'master_date_to',
|
||||
'slave_date_from',
|
||||
'slave_date_to',
|
||||
'start_date',
|
||||
mode='before',
|
||||
)
|
||||
@classmethod
|
||||
def _normalize_optional_date_text(cls, value):
|
||||
if value is None:
|
||||
return None
|
||||
text = str(value).strip()
|
||||
return text or None
|
||||
|
||||
@field_validator('aoi_overlap_threshold', mode='before')
|
||||
@classmethod
|
||||
def _normalize_aoi_overlap_threshold(cls, value):
|
||||
if value is None:
|
||||
return None
|
||||
if isinstance(value, str):
|
||||
text = value.strip()
|
||||
if not text:
|
||||
return None
|
||||
value = text
|
||||
try:
|
||||
numeric = float(value)
|
||||
except (TypeError, ValueError):
|
||||
return value
|
||||
return None if numeric <= 0 else numeric
|
||||
|
||||
@field_validator('allowed_satellites', mode='before')
|
||||
@classmethod
|
||||
def _normalize_allowed_satellites(cls, value):
|
||||
if value in (None, '', []):
|
||||
return None
|
||||
if not isinstance(value, list):
|
||||
return value
|
||||
normalized = [str(item).strip() for item in value if str(item).strip()]
|
||||
return normalized or None
|
||||
|
||||
@field_validator('master_date_to')
|
||||
@classmethod
|
||||
def validate_master_date_range(cls, v, info):
|
||||
if v and info.data.get('master_date_from'):
|
||||
if v < info.data['master_date_from']:
|
||||
raise ValueError('master_date_to must >= master_date_from')
|
||||
return v
|
||||
|
||||
@field_validator('slave_date_to')
|
||||
@classmethod
|
||||
def validate_slave_date_range(cls, v, info):
|
||||
if v and info.data.get('slave_date_from'):
|
||||
if v < info.data['slave_date_from']:
|
||||
raise ValueError('slave_date_to must >= slave_date_from')
|
||||
return v
|
||||
|
||||
|
||||
class RadarPair(BaseModel):
|
||||
"""单个雷达干涉对的数据模型。"""
|
||||
master: RadarData
|
||||
slave: RadarData
|
||||
task_name: str
|
||||
task_alias: Optional[str] = None
|
||||
pair_key: Optional[str] = None
|
||||
pair_uid: Optional[str] = None
|
||||
metric_cache_ref_id: Optional[int] = None
|
||||
network_run_id: Optional[str] = None
|
||||
network_edge_id: Optional[int] = None
|
||||
policy_version: Optional[str] = None
|
||||
selection_strategy: Optional[str] = None
|
||||
selection_score: Optional[float] = None
|
||||
selection_reason: Optional[str] = None
|
||||
time_baseline_days: int
|
||||
spatial_baseline_meters: float
|
||||
|
||||
|
||||
class PairingResponse(BaseModel):
|
||||
"""配对结果的响应模型,包含配对列表和可选的 AOI GeoJSON。"""
|
||||
pairs: List[RadarPair]
|
||||
aoi_geojson: Optional[dict] = None
|
||||
warnings: List[str] = Field(default_factory=list)
|
||||
fallback_used: bool = False
|
||||
degraded: bool = False
|
||||
policy_version: Optional[str] = None
|
||||
network_run_id: Optional[str] = None
|
||||
candidate_count: int = 0
|
||||
selected_edge_count: int = 0
|
||||
|
||||
|
||||
class PsRequest(BaseModel):
|
||||
"""PS-InSAR 时序分析数据准备的请求模型。"""
|
||||
initial_overlap_threshold: float = 0.3
|
||||
final_overlap_threshold: float = 0.95
|
||||
|
||||
|
||||
class TaskInfo(BaseModel):
|
||||
"""任务信息的 Pydantic 模型,用于 API 返回。"""
|
||||
task_id: str
|
||||
task_type: str
|
||||
task_name: str
|
||||
status: str
|
||||
progress: int
|
||||
message: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
started_at: Optional[datetime] = None
|
||||
ended_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class AuthUserInfo(BaseModel):
|
||||
id: int
|
||||
username: str
|
||||
role: str
|
||||
is_active: bool
|
||||
created_at: datetime
|
||||
last_login_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class AuthAuditLogInfo(BaseModel):
|
||||
id: int
|
||||
user_id: Optional[int] = None
|
||||
username: Optional[str] = None
|
||||
action: str
|
||||
resource: Optional[str] = None
|
||||
detail: Optional[Dict[str, Any]] = None
|
||||
ip_address: Optional[str] = None
|
||||
created_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class RadarPreviewStatusInfo(BaseModel):
|
||||
radar_id: int
|
||||
status: str
|
||||
cache_version: Optional[str] = None
|
||||
cache_updated_at: Optional[datetime] = None
|
||||
has_geo_cache: bool = False
|
||||
has_raw_cache: bool = False
|
||||
source_found: bool = False
|
||||
fallback_in_use: bool = False
|
||||
message: Optional[str] = None
|
||||
error: Optional[str] = None
|
||||
|
||||
|
||||
class DinsarTaskBatch(BaseModel):
|
||||
batch_id: str
|
||||
name: Optional[str] = None
|
||||
status: str
|
||||
total_items: int
|
||||
completed_items: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class DinsarTaskItem(BaseModel):
|
||||
id: int
|
||||
batch_id: str
|
||||
task_name: Optional[str] = None
|
||||
task_alias: Optional[str] = None
|
||||
pair_key: Optional[str] = None
|
||||
scene_pair_uid: Optional[str] = None
|
||||
network_run_id: Optional[str] = None
|
||||
network_edge_id: Optional[int] = None
|
||||
policy_version: Optional[str] = None
|
||||
selection_strategy: Optional[str] = None
|
||||
master_path: str
|
||||
slave_path: str
|
||||
master_satellite: Optional[str] = None
|
||||
master_imaging_date: Optional[str] = None
|
||||
master_imaging_mode: Optional[str] = None
|
||||
master_polarization: Optional[str] = None
|
||||
slave_satellite: Optional[str] = None
|
||||
slave_imaging_date: Optional[str] = None
|
||||
slave_imaging_mode: Optional[str] = None
|
||||
slave_polarization: Optional[str] = None
|
||||
time_baseline_days: Optional[int] = None
|
||||
spatial_baseline_meters: Optional[float] = None
|
||||
status: str
|
||||
remark: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class PsTaskBatch(BaseModel):
|
||||
batch_id: str
|
||||
name: Optional[str] = None
|
||||
direction: Optional[str] = None
|
||||
status: str
|
||||
total_items: int
|
||||
completed_items: int
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class PsTaskItem(BaseModel):
|
||||
id: int
|
||||
batch_id: str
|
||||
file_path: str
|
||||
satellite: Optional[str] = None
|
||||
imaging_date: Optional[str] = None
|
||||
polarization: Optional[str] = None
|
||||
has_orbit_data: bool
|
||||
status: str
|
||||
remark: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class PsTimeseriesRun(BaseModel):
|
||||
run_id: str
|
||||
batch_id: str
|
||||
run_name: str
|
||||
catalog_name: str
|
||||
mode: str
|
||||
engine_code: str
|
||||
processor_code: str
|
||||
env_name: Optional[str] = None
|
||||
wsl_distro: Optional[str] = None
|
||||
status: str
|
||||
task_id: Optional[str] = None
|
||||
workflow_run_id: Optional[str] = None
|
||||
direction: Optional[str] = None
|
||||
stack_size: int
|
||||
reference_date: Optional[str] = None
|
||||
water_mask_mode: Optional[str] = None
|
||||
dem_path_windows: Optional[str] = None
|
||||
dem_path_wsl: Optional[str] = None
|
||||
orbit_pool_windows: Optional[str] = None
|
||||
orbit_pool_wsl: Optional[str] = None
|
||||
work_root_windows: Optional[str] = None
|
||||
work_root_wsl: Optional[str] = None
|
||||
publish_dir_windows: Optional[str] = None
|
||||
publish_dir_wsl: Optional[str] = None
|
||||
manifest_path_windows: Optional[str] = None
|
||||
manifest_path_wsl: Optional[str] = None
|
||||
params_json: Optional[Dict[str, Any]] = None
|
||||
summary_json: Optional[Dict[str, Any]] = None
|
||||
input_snapshot_json: Optional[Dict[str, Any]] = None
|
||||
orbit_summary_json: Optional[Dict[str, Any]] = None
|
||||
quality_summary_json: Optional[Dict[str, Any]] = None
|
||||
error_message: Optional[str] = None
|
||||
created_by: Optional[str] = None
|
||||
created_at: datetime
|
||||
updated_at: datetime
|
||||
started_at: Optional[datetime] = None
|
||||
ended_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# ============ AI 诊断 Schema ============
|
||||
|
||||
|
||||
# ============ 水体检测 Schema ============
|
||||
|
||||
|
||||
class WaterDetectRequest(BaseModel):
|
||||
"""水体检测请求"""
|
||||
scene_id: Optional[int] = Field(None, description="SARSceneGeoORM 主键(可选)")
|
||||
input_path: Optional[str] = Field(None, description="直接指定 GeoTIFF 路径(可选)")
|
||||
|
||||
|
||||
class WaterDetectResponse(BaseModel):
|
||||
"""水体检测响应"""
|
||||
id: int
|
||||
scene_id: Optional[int] = None
|
||||
input_path: Optional[str] = None
|
||||
output_path: Optional[str] = None
|
||||
water_area_km2: Optional[float] = None
|
||||
water_pixel_count: Optional[int] = None
|
||||
otsu_threshold_db: Optional[float] = None
|
||||
status: str
|
||||
error_msg: Optional[str] = None
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# ============ GF3 处理 Schema ============
|
||||
|
||||
|
||||
class GF3ProcessRequest(BaseModel):
|
||||
"""GF3 L1A→L2 处理请求"""
|
||||
input_dir: str = Field(..., description="GF3 L1A 数据目录路径")
|
||||
resolution: float = Field(default=0.0002, ge=0.00001, le=0.01, description="输出分辨率(度)")
|
||||
|
||||
|
||||
class GF3ProcessResponse(BaseModel):
|
||||
"""GF3 处理响应"""
|
||||
id: int
|
||||
input_dir: str
|
||||
output_dir: Optional[str] = None
|
||||
polarizations: Optional[str] = None
|
||||
l2_paths: Optional[str] = None
|
||||
resolution: float = 0.0002
|
||||
status: str
|
||||
error_msg: Optional[str] = None
|
||||
created_at: Optional[datetime] = None
|
||||
updated_at: Optional[datetime] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
# ============ AI 诊断 Schema ============
|
||||
|
||||
|
||||
class AiDiagnosisCreate(BaseModel):
|
||||
"""创建 AI 诊断请求"""
|
||||
result_id: int = Field(..., description="D-InSAR 结果 ID")
|
||||
model_name: str = Field(..., description="模型名称,如 llama3.2-vision")
|
||||
prompt_template: str = Field(..., description="Prompt 模板名称:quick/standard/detailed")
|
||||
custom_prompt: Optional[str] = Field(None, description="自定义 Prompt(覆盖模板)")
|
||||
|
||||
|
||||
class AiDiagnosisResponse(BaseModel):
|
||||
"""AI 诊断响应"""
|
||||
id: int
|
||||
result_id: int
|
||||
product_ref_id: Optional[int] = None
|
||||
product_id: Optional[str] = None
|
||||
task_id: Optional[str] = None
|
||||
model_name: str
|
||||
prompt_template: str
|
||||
prompt_text: Optional[str] = None
|
||||
diagnosis_markdown: Optional[str] = None
|
||||
risk_level: Optional[str] = None
|
||||
confidence_score: Optional[float] = None
|
||||
result_name: Optional[str] = None
|
||||
date_range: Optional[str] = None
|
||||
quality_score: Optional[float] = None
|
||||
hazards_found: int = 0
|
||||
hazards_snapshot: Optional[List[Dict[str, Any]]] = None
|
||||
created_at: datetime
|
||||
duration_seconds: Optional[float] = None
|
||||
error_message: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
|
||||
class AiDiagnosisListResponse(BaseModel):
|
||||
"""AI 诊断列表响应"""
|
||||
items: List[AiDiagnosisResponse]
|
||||
total: int
|
||||
page: int
|
||||
page_size: int
|
||||
Reference in New Issue
Block a user