1765 lines
79 KiB
Python
1765 lines
79 KiB
Python
"""
|
||
SQLAlchemy ORM 模型定义。
|
||
所有数据库表对应的 ORM 类均在此文件中定义。
|
||
"""
|
||
from sqlalchemy import (
|
||
Column, Integer, BigInteger, String, Boolean, Float, JSON, Text, LargeBinary,
|
||
DateTime, func, ForeignKey, UniqueConstraint, Index,
|
||
)
|
||
from sqlalchemy.orm import relationship
|
||
from geoalchemy2 import Geometry
|
||
|
||
from ..database import Base
|
||
|
||
|
||
class RadarDataORM(Base):
|
||
"""
|
||
SQLAlchemy ORM Model for storing radar data metadata in the database.
|
||
"""
|
||
__tablename__ = "radar_data"
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
unique_id = Column(String, unique=True, index=True)
|
||
|
||
satellite = Column(String, index=True)
|
||
imaging_date = Column(String, index=True)
|
||
imaging_mode = Column(String)
|
||
orbit_direction = Column(String, index=True, nullable=True)
|
||
polarization = Column(String)
|
||
satellite_mode = Column(String, nullable=True)
|
||
receiving_station = Column(String, nullable=True)
|
||
orbit_circle = Column(String, nullable=True)
|
||
scene_center_lon = Column(Float, nullable=True)
|
||
scene_center_lat = Column(Float, nullable=True)
|
||
acquisition_time_utc = Column(String, nullable=True)
|
||
product_type = Column(String, nullable=True)
|
||
source_product_token = Column(String, nullable=True)
|
||
image_data_type = Column(String, nullable=True)
|
||
image_data_format = Column(String, nullable=True)
|
||
product_variant = Column(String, nullable=True)
|
||
product_level = Column(String, nullable=True)
|
||
product_unique_id = Column(String, nullable=True)
|
||
satellite_family = Column(String, index=True, nullable=True)
|
||
look_direction = Column(String, index=True, nullable=True)
|
||
acquisition_start_time_utc = Column(DateTime, nullable=True, index=True)
|
||
acquisition_stop_time_utc = Column(DateTime, nullable=True)
|
||
absolute_orbit = Column(String, nullable=True, index=True)
|
||
relative_orbit = Column(String, nullable=True, index=True)
|
||
source_format = Column(String(32), nullable=True, index=True)
|
||
source_product_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("source_product_assets.id", ondelete="SET NULL"),
|
||
nullable=True,
|
||
index=True,
|
||
)
|
||
source_archive_asset_id = Column(
|
||
Integer,
|
||
ForeignKey("source_product_assets.id", ondelete="SET NULL"),
|
||
nullable=True,
|
||
index=True,
|
||
)
|
||
selected_orbit_asset_id = Column(
|
||
Integer,
|
||
ForeignKey("orbit_assets.id", ondelete="SET NULL"),
|
||
nullable=True,
|
||
index=True,
|
||
)
|
||
orbit_binding_status = Column(String(32), nullable=False, default="UNBOUND", server_default="UNBOUND", index=True)
|
||
orbit_binding_reason = Column(Text, nullable=True)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
geocoded_flag = Column(Boolean, nullable=True)
|
||
insar_source_ready = Column(Boolean, nullable=False, default=False, server_default="false")
|
||
insar_source_reason = Column(Text, nullable=True)
|
||
file_path = Column(String, unique=True)
|
||
has_orbit_data = Column(Boolean)
|
||
orbit_file_path = Column(String, nullable=True)
|
||
is_envi_processed = Column(Boolean, default=False)
|
||
|
||
geom = Column(Geometry('POLYGON', srid=4326), index=True)
|
||
coverage_polygon = Column(JSON)
|
||
|
||
min_lon = Column(Float)
|
||
min_lat = Column(Float)
|
||
max_lon = Column(Float)
|
||
max_lat = Column(Float)
|
||
|
||
preview_cache_status = Column(String, nullable=False, default="NONE", server_default="NONE")
|
||
preview_cache_version = Column(String, nullable=True)
|
||
preview_cache_path = Column(String, nullable=True)
|
||
preview_cache_updated_at = Column(DateTime, nullable=True)
|
||
preview_cache_error = Column(Text, nullable=True)
|
||
|
||
source_product_asset = relationship("SourceProductAssetORM", foreign_keys=[source_product_ref_id])
|
||
source_archive_asset = relationship("SourceProductAssetORM", foreign_keys=[source_archive_asset_id])
|
||
selected_orbit_asset = relationship("OrbitAssetORM", foreign_keys=[selected_orbit_asset_id])
|
||
|
||
|
||
class DinsarResultORM(Base):
|
||
__tablename__ = 'dinsar_results'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
name = Column(String, index=True)
|
||
compat_product_id = Column(String(64), nullable=True)
|
||
file_path = Column(String, unique=True)
|
||
min_lon = Column(Float)
|
||
min_lat = Column(Float)
|
||
max_lon = Column(Float)
|
||
max_lat = Column(Float)
|
||
|
||
geom = Column(Geometry('POLYGON', srid=4326), index=True)
|
||
coverage_polygon = Column(JSON)
|
||
|
||
is_cached = Column(Boolean, default=False, nullable=False)
|
||
|
||
ai_score = Column(Float, nullable=True)
|
||
user_label = Column(Integer, nullable=True)
|
||
ai_report = Column(Text, nullable=True)
|
||
|
||
|
||
class ResultProductORM(Base):
|
||
__tablename__ = "result_products"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
product_id = Column(String(64), unique=True, index=True, nullable=False)
|
||
catalog_name = Column(String(32), index=True, nullable=False, default="dinsar")
|
||
product_family = Column(String(32), index=True, nullable=True)
|
||
product_type = Column(String(32), index=True, nullable=False, default="dinsar")
|
||
display_name = Column(String(255), nullable=False)
|
||
task_name = Column(String(255), index=True, nullable=True)
|
||
task_alias = Column(String(255), index=True, nullable=True)
|
||
pair_key = Column(String(128), index=True, nullable=True)
|
||
stack_key = Column(String(128), index=True, nullable=True)
|
||
pair_uid = Column(String(64), index=True, nullable=True)
|
||
run_key = Column(String(128), index=True, nullable=True)
|
||
network_run_id = Column(String(64), index=True, nullable=True)
|
||
network_edge_id = Column(Integer, nullable=True)
|
||
policy_version = Column(String(32), index=True, nullable=True)
|
||
selection_strategy = Column(String(32), index=True, nullable=True)
|
||
profile_code = Column(String(64), index=True, nullable=True)
|
||
engine_code = Column(String(32), index=True, nullable=False)
|
||
engine_version = Column(String(64), nullable=True)
|
||
package_schema = Column(String(64), nullable=True)
|
||
package_layout = Column(String(64), nullable=True)
|
||
processor_code = Column(String(64), nullable=True)
|
||
runtime_id = Column(String(64), nullable=True)
|
||
status = Column(String(32), index=True, nullable=False, default="READY")
|
||
health_status = Column(String(16), index=True, nullable=False, default="OK")
|
||
|
||
publish_dir = Column(String, unique=True, nullable=False)
|
||
manifest_path = Column(String, unique=True, nullable=False)
|
||
source_primary_path = Column(String, nullable=True)
|
||
native_output_dir = Column(String, nullable=True)
|
||
preview_path = Column(String, nullable=True)
|
||
primary_asset_path = Column(String, nullable=True)
|
||
|
||
summary_json = Column(JSON, nullable=True)
|
||
tags_json = Column(JSON, nullable=True)
|
||
|
||
ai_score = Column(Float, nullable=True)
|
||
user_label = Column(Integer, nullable=True)
|
||
|
||
min_lon = Column(Float, nullable=True)
|
||
min_lat = Column(Float, nullable=True)
|
||
max_lon = Column(Float, nullable=True)
|
||
max_lat = Column(Float, nullable=True)
|
||
geom = Column(Geometry("POLYGON", srid=4326), nullable=True)
|
||
coverage_polygon = Column(JSON, nullable=True)
|
||
|
||
produced_at = Column(DateTime, nullable=True)
|
||
published_at = Column(DateTime, nullable=True)
|
||
registered_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
profile = relationship(
|
||
"DinsarProductProfileORM",
|
||
back_populates="product",
|
||
cascade="all, delete-orphan",
|
||
uselist=False,
|
||
)
|
||
assets = relationship(
|
||
"ResultAssetORM",
|
||
back_populates="product",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
issues = relationship(
|
||
"ResultIssueORM",
|
||
back_populates="product",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
|
||
__table_args__ = (
|
||
Index("idx_result_products_catalog_status", "catalog_name", "status"),
|
||
Index("idx_result_products_engine_dates", "engine_code", "published_at"),
|
||
)
|
||
|
||
|
||
class DinsarProductProfileORM(Base):
|
||
__tablename__ = "dinsar_product_profiles"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
product_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("result_products.id", ondelete="CASCADE"),
|
||
unique=True,
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
|
||
master_path = Column(String, nullable=True)
|
||
slave_path = Column(String, nullable=True)
|
||
master_satellite = Column(String, nullable=True)
|
||
slave_satellite = Column(String, nullable=True)
|
||
master_imaging_date = Column(String(8), index=True, nullable=True)
|
||
slave_imaging_date = Column(String(8), index=True, nullable=True)
|
||
master_imaging_mode = Column(String, nullable=True)
|
||
slave_imaging_mode = Column(String, nullable=True)
|
||
master_polarization = Column(String, nullable=True)
|
||
slave_polarization = Column(String, nullable=True)
|
||
orbit_direction = Column(String, index=True, nullable=True)
|
||
time_baseline_days = Column(Integer, index=True, nullable=True)
|
||
spatial_baseline_meters = Column(Float, index=True, nullable=True)
|
||
scene_center_distance_meters = Column(Float, index=True, nullable=True)
|
||
|
||
grid_size_m = Column(Float, nullable=True)
|
||
radar_wavelength = Column(Float, nullable=True)
|
||
orbit_clip_margin = Column(Integer, nullable=True)
|
||
bbox_margin = Column(Float, nullable=True)
|
||
coherence_threshold = Column(Float, nullable=True)
|
||
|
||
params_json = Column(JSON, nullable=True)
|
||
metrics_json = Column(JSON, nullable=True)
|
||
|
||
product = relationship("ResultProductORM", back_populates="profile")
|
||
|
||
|
||
class ResultAssetORM(Base):
|
||
__tablename__ = "result_assets"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
product_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("result_products.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
asset_role = Column(String(32), index=True, nullable=False)
|
||
asset_name = Column(String(255), nullable=False)
|
||
relative_path = Column(String, nullable=False)
|
||
absolute_path = Column(String, nullable=False)
|
||
format = Column(String(32), nullable=True)
|
||
media_type = Column(String(64), nullable=True)
|
||
is_required = Column(Boolean, nullable=False, default=False)
|
||
is_primary = Column(Boolean, nullable=False, default=False)
|
||
exists_flag = Column(Boolean, nullable=False, default=True, index=True)
|
||
file_size = Column(BigInteger, nullable=True)
|
||
checksum_sha256 = Column(String(64), nullable=True)
|
||
band_count = Column(Integer, nullable=True)
|
||
width = Column(Integer, nullable=True)
|
||
height = Column(Integer, nullable=True)
|
||
srid = Column(Integer, nullable=True)
|
||
nodata = Column(Float, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
product = relationship("ResultProductORM", back_populates="assets")
|
||
issues = relationship(
|
||
"ResultIssueORM",
|
||
back_populates="asset",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
|
||
__table_args__ = (
|
||
Index("idx_result_assets_product_role", "product_ref_id", "asset_role"),
|
||
)
|
||
|
||
|
||
class ResultIssueORM(Base):
|
||
__tablename__ = "result_issues"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
product_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("result_products.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
asset_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("result_assets.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=True,
|
||
)
|
||
issue_code = Column(String(64), index=True, nullable=False)
|
||
severity = Column(String(16), index=True, nullable=False, default="WARN")
|
||
status = Column(String(16), index=True, nullable=False, default="OPEN")
|
||
scope = Column(String(16), nullable=False, default="file")
|
||
message = Column(Text, nullable=False)
|
||
repair_action = Column(String(64), nullable=True)
|
||
repair_payload = Column(JSON, nullable=True)
|
||
detected_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
resolved_at = Column(DateTime, nullable=True)
|
||
|
||
product = relationship("ResultProductORM", back_populates="issues")
|
||
asset = relationship("ResultAssetORM", back_populates="issues")
|
||
|
||
|
||
class ResultCatalogStateORM(Base):
|
||
__tablename__ = "result_catalog_states"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
catalog_name = Column(String(32), unique=True, index=True, nullable=False)
|
||
product_family = Column(String(32), index=True, nullable=True)
|
||
storage_root = Column(String, nullable=False)
|
||
status = Column(String(16), index=True, nullable=False, default="READY")
|
||
needs_rebuild = Column(Boolean, nullable=False, default=False)
|
||
manifest_count = Column(Integer, nullable=False, default=0)
|
||
manifest_fingerprint = Column(String(64), nullable=True)
|
||
db_count = Column(Integer, nullable=False, default=0)
|
||
issue_count = Column(Integer, nullable=False, default=0)
|
||
last_message = Column(Text, nullable=True)
|
||
last_boot_check_at = Column(DateTime, nullable=True)
|
||
last_full_rebuild_at = Column(DateTime, nullable=True)
|
||
last_incremental_scan_at = Column(DateTime, nullable=True)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class PairingCacheStateORM(Base):
|
||
__tablename__ = "pairing_cache_state"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
cache_scope = Column(String(32), unique=True, index=True, nullable=False, default="global")
|
||
metric_version = Column(String(32), nullable=False, default="2026.05.raw.v1")
|
||
status = Column(String(16), index=True, nullable=False, default="DIRTY")
|
||
scene_count = Column(Integer, nullable=False, default=0)
|
||
pair_count = Column(Integer, nullable=False, default=0)
|
||
dirty_scene_count = Column(Integer, nullable=False, default=0)
|
||
last_full_rebuild_at = Column(DateTime, nullable=True)
|
||
last_incremental_reconcile_at = Column(DateTime, nullable=True)
|
||
last_error = Column(Text, nullable=True)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class PairingDirtySceneORM(Base):
|
||
__tablename__ = "pairing_dirty_scenes"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
scene_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("radar_data.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
scene_uid = Column(String, index=True, nullable=False)
|
||
reason = Column(String(64), nullable=False, default="scan")
|
||
status = Column(String(16), index=True, nullable=False, default="PENDING")
|
||
marked_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
resolved_at = Column(DateTime, nullable=True)
|
||
|
||
__table_args__ = (
|
||
Index("idx_pairing_dirty_scenes_scene_status", "scene_ref_id", "status"),
|
||
)
|
||
|
||
|
||
class PairingMetricCacheORM(Base):
|
||
__tablename__ = "pairing_metric_cache"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
master_scene_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("radar_data.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
slave_scene_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("radar_data.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
master_scene_uid = Column(String, index=True, nullable=False)
|
||
slave_scene_uid = Column(String, index=True, nullable=False)
|
||
pair_uid = Column(String, index=True, nullable=False)
|
||
metric_version = Column(String(32), index=True, nullable=False, default="2026.05.raw.v1")
|
||
orientation_rule_version = Column(String(32), nullable=False, default="date_then_scene_uid_v1")
|
||
time_baseline_days = Column(Integer, index=True, nullable=True)
|
||
spatial_baseline_meters = Column(Float, index=True, nullable=True)
|
||
scene_center_distance_meters = Column(Float, index=True, nullable=True)
|
||
scene_overlap_ratio = Column(Float, index=True, nullable=True)
|
||
pair_aoi_overlap_ratio = Column(Float, nullable=True)
|
||
orbit_direction = Column(String, index=True, nullable=True)
|
||
same_relative_orbit = Column(Boolean, nullable=False, default=False, server_default="false", index=True)
|
||
master_relative_orbit = Column(String(64), nullable=True)
|
||
slave_relative_orbit = Column(String(64), nullable=True)
|
||
dinsar_quality_tier = Column(String(16), nullable=False, default="C", server_default="C", index=True)
|
||
dinsar_quality_score = Column(Float, nullable=True)
|
||
dinsar_readiness = Column(String(32), nullable=False, default="CANDIDATE", server_default="CANDIDATE", index=True)
|
||
dinsar_reasons_json = Column(JSON, nullable=True)
|
||
same_satellite = Column(Boolean, nullable=False, default=True)
|
||
same_satellite_family = Column(Boolean, nullable=False, default=True, server_default="true")
|
||
same_look_direction = Column(Boolean, nullable=False, default=True, server_default="true")
|
||
same_imaging_mode = Column(Boolean, nullable=False, default=True)
|
||
same_polarization = Column(Boolean, nullable=False, default=True)
|
||
master_imaging_date = Column(String(8), index=True, nullable=True)
|
||
slave_imaging_date = Column(String(8), index=True, nullable=True)
|
||
master_satellite = Column(String, index=True, nullable=True)
|
||
slave_satellite = Column(String, index=True, nullable=True)
|
||
master_satellite_family = Column(String, index=True, nullable=True)
|
||
slave_satellite_family = Column(String, index=True, nullable=True)
|
||
master_imaging_mode = Column(String, nullable=True)
|
||
slave_imaging_mode = Column(String, nullable=True)
|
||
master_polarization = Column(String, nullable=True)
|
||
slave_polarization = Column(String, nullable=True)
|
||
master_look_direction = Column(String, nullable=True)
|
||
slave_look_direction = Column(String, nullable=True)
|
||
master_file_path = Column(String, nullable=True)
|
||
slave_file_path = Column(String, nullable=True)
|
||
status = Column(String(16), index=True, nullable=False, default="READY")
|
||
computed_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
|
||
edges = relationship("PairingNetworkEdgeORM", back_populates="metric_cache")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint(
|
||
"master_scene_ref_id",
|
||
"slave_scene_ref_id",
|
||
"metric_version",
|
||
name="uq_pairing_metric_cache_pair_version",
|
||
),
|
||
Index(
|
||
"idx_pairing_metric_cache_metric_dates",
|
||
"metric_version",
|
||
"master_imaging_date",
|
||
"slave_imaging_date",
|
||
),
|
||
)
|
||
|
||
|
||
class PairingNetworkRunORM(Base):
|
||
__tablename__ = "pairing_network_runs"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
network_run_id = Column(String(64), unique=True, index=True, nullable=False)
|
||
strategy = Column(String(32), index=True, nullable=False)
|
||
policy_version = Column(String(32), index=True, nullable=False)
|
||
request_hash = Column(String(64), index=True, nullable=True)
|
||
request_params_json = Column(JSON, nullable=True)
|
||
aoi_source = Column(String(32), nullable=True)
|
||
aoi_hash = Column(String(64), index=True, nullable=True)
|
||
aoi_summary_json = Column(JSON, nullable=True)
|
||
candidate_count = Column(Integer, nullable=False, default=0)
|
||
selected_edge_count = Column(Integer, nullable=False, default=0)
|
||
warning_count = Column(Integer, nullable=False, default=0)
|
||
status = Column(String(16), index=True, nullable=False, default="PENDING")
|
||
fallback_used = Column(Boolean, nullable=False, default=False)
|
||
created_by = Column(String(64), nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
edges = relationship(
|
||
"PairingNetworkEdgeORM",
|
||
back_populates="network_run",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
|
||
|
||
class PairingNetworkEdgeORM(Base):
|
||
__tablename__ = "pairing_network_edges"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
network_run_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("pairing_network_runs.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
metric_cache_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("pairing_metric_cache.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
edge_rank = Column(Integer, nullable=False, default=0)
|
||
selection_reason = Column(String(64), nullable=True)
|
||
selection_score = Column(Float, nullable=True)
|
||
selection_meta_json = Column(JSON, nullable=True)
|
||
is_reference_edge = Column(Boolean, nullable=False, default=False)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
|
||
network_run = relationship("PairingNetworkRunORM", back_populates="edges")
|
||
metric_cache = relationship("PairingMetricCacheORM", back_populates="edges")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint(
|
||
"network_run_ref_id",
|
||
"metric_cache_ref_id",
|
||
name="uq_pairing_network_edges_run_metric",
|
||
),
|
||
Index("idx_pairing_network_edges_run_rank", "network_run_ref_id", "edge_rank"),
|
||
)
|
||
|
||
|
||
class TimeseriesStackPlanORM(Base):
|
||
__tablename__ = "timeseries_stack_plans"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
plan_id = Column(String(64), unique=True, index=True, nullable=False)
|
||
strategy = Column(String(32), index=True, nullable=False, default="sbas_stack")
|
||
request_hash = Column(String(64), index=True, nullable=True)
|
||
request_params_json = Column(JSON, nullable=True)
|
||
aoi_source = Column(String(32), nullable=True)
|
||
aoi_hash = Column(String(64), index=True, nullable=True)
|
||
aoi_summary_json = Column(JSON, nullable=True)
|
||
direction = Column(String(32), index=True, nullable=True)
|
||
scene_count = Column(Integer, nullable=False, default=0)
|
||
stack_key = Column(String(128), index=True, nullable=True)
|
||
group_key = Column(String(128), index=True, nullable=True)
|
||
status = Column(String(16), index=True, nullable=False, default="READY")
|
||
created_by = Column(String(64), nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
items = relationship(
|
||
"TimeseriesStackPlanItemORM",
|
||
back_populates="plan",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
edges = relationship(
|
||
"TimeseriesStackPlanEdgeORM",
|
||
back_populates="plan",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
|
||
__table_args__ = (
|
||
Index("idx_timeseries_stack_plans_direction_created", "direction", "created_at"),
|
||
)
|
||
|
||
|
||
class TimeseriesStackPlanItemORM(Base):
|
||
__tablename__ = "timeseries_stack_plan_items"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
plan_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("timeseries_stack_plans.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
radar_data_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("radar_data.id", ondelete="SET NULL"),
|
||
index=True,
|
||
nullable=True,
|
||
)
|
||
scene_rank = Column(Integer, nullable=False, default=0)
|
||
file_path = Column(String, nullable=False)
|
||
satellite = Column(String, nullable=True)
|
||
imaging_date = Column(String, nullable=True)
|
||
imaging_mode = Column(String, nullable=True)
|
||
polarization = Column(String, nullable=True)
|
||
has_orbit_data = Column(Boolean, nullable=False, default=False)
|
||
selection_meta_json = Column(JSON, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
|
||
plan = relationship("TimeseriesStackPlanORM", back_populates="items")
|
||
radar_data = relationship("RadarDataORM")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("plan_ref_id", "scene_rank", name="uq_timeseries_plan_items_plan_rank"),
|
||
Index("idx_timeseries_plan_items_plan_date", "plan_ref_id", "imaging_date"),
|
||
)
|
||
|
||
|
||
class TimeseriesStackPlanEdgeORM(Base):
|
||
__tablename__ = "timeseries_stack_plan_edges"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
plan_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("timeseries_stack_plans.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
master_plan_item_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("timeseries_stack_plan_items.id", ondelete="SET NULL"),
|
||
index=True,
|
||
nullable=True,
|
||
)
|
||
slave_plan_item_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("timeseries_stack_plan_items.id", ondelete="SET NULL"),
|
||
index=True,
|
||
nullable=True,
|
||
)
|
||
metric_cache_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("pairing_metric_cache.id", ondelete="SET NULL"),
|
||
index=True,
|
||
nullable=True,
|
||
)
|
||
master_scene_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("radar_data.id", ondelete="SET NULL"),
|
||
index=True,
|
||
nullable=True,
|
||
)
|
||
slave_scene_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("radar_data.id", ondelete="SET NULL"),
|
||
index=True,
|
||
nullable=True,
|
||
)
|
||
edge_rank = Column(Integer, nullable=False, default=0)
|
||
master_imaging_date = Column(String(8), index=True, nullable=True)
|
||
slave_imaging_date = Column(String(8), index=True, nullable=True)
|
||
temporal_baseline_days = Column(Integer, nullable=True)
|
||
spatial_baseline_meters = Column(Float, nullable=True)
|
||
perpendicular_baseline_meters = Column(Float, nullable=True)
|
||
scene_overlap_ratio = Column(Float, nullable=True)
|
||
pair_aoi_overlap_ratio = Column(Float, nullable=True)
|
||
selection_reason = Column(String(64), nullable=True)
|
||
selection_score = Column(Float, nullable=True)
|
||
selection_meta_json = Column(JSON, nullable=True)
|
||
enabled = Column(Boolean, nullable=False, default=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
|
||
plan = relationship("TimeseriesStackPlanORM", back_populates="edges")
|
||
master_plan_item = relationship("TimeseriesStackPlanItemORM", foreign_keys=[master_plan_item_ref_id])
|
||
slave_plan_item = relationship("TimeseriesStackPlanItemORM", foreign_keys=[slave_plan_item_ref_id])
|
||
metric_cache = relationship("PairingMetricCacheORM")
|
||
master_scene = relationship("RadarDataORM", foreign_keys=[master_scene_ref_id])
|
||
slave_scene = relationship("RadarDataORM", foreign_keys=[slave_scene_ref_id])
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint(
|
||
"plan_ref_id",
|
||
"edge_rank",
|
||
name="uq_timeseries_plan_edges_plan_rank",
|
||
),
|
||
Index("idx_timeseries_plan_edges_plan_enabled", "plan_ref_id", "enabled"),
|
||
Index(
|
||
"idx_timeseries_plan_edges_plan_scenes",
|
||
"plan_ref_id",
|
||
"master_scene_ref_id",
|
||
"slave_scene_ref_id",
|
||
),
|
||
)
|
||
|
||
|
||
class HazardPointORM(Base):
|
||
__tablename__ = 'hazard_points'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
tybh = Column(String, unique=True, index=True)
|
||
hazard_type = Column(String, index=True)
|
||
hazard_name = Column(String, index=True)
|
||
city = Column(String, index=True)
|
||
county = Column(String, index=True)
|
||
township = Column(String)
|
||
longitude = Column(Float)
|
||
latitude = Column(Float)
|
||
|
||
geom = Column(Geometry('POINT', srid=4326), index=True)
|
||
|
||
|
||
class SystemTaskORM(Base):
|
||
"""
|
||
持久化任务模型。
|
||
用于跟踪后台任务(如扫描、拷贝、AI训练)的状态。
|
||
"""
|
||
__tablename__ = "system_tasks"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
task_id = Column(String, unique=True, index=True, nullable=False)
|
||
task_type = Column(String, index=True, nullable=False)
|
||
task_name = Column(String, index=True, nullable=False)
|
||
status = Column(String, nullable=False, default="PENDING")
|
||
progress = Column(Integer, default=0)
|
||
message = Column(Text, nullable=True)
|
||
params = Column(JSON, nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
started_at = Column(DateTime, nullable=True)
|
||
ended_at = Column(DateTime, nullable=True)
|
||
|
||
logs = relationship("TaskLogORM", back_populates="task", cascade="all, delete-orphan")
|
||
|
||
|
||
class TaskLogORM(Base):
|
||
"""
|
||
任务日志模型。
|
||
记录后台任务执行过程中产生的日志条目。
|
||
"""
|
||
__tablename__ = "task_logs"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
task_id = Column(String, ForeignKey('system_tasks.task_id'), index=True, nullable=False)
|
||
log_level = Column(String, default="INFO")
|
||
message = Column(Text, nullable=False)
|
||
timestamp = Column(DateTime, server_default=func.now())
|
||
|
||
task = relationship("SystemTaskORM", back_populates="logs")
|
||
|
||
|
||
class SystemJobORM(Base):
|
||
"""
|
||
Persistent job queue entry for background workers.
|
||
"""
|
||
__tablename__ = "system_jobs"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
job_id = Column(String, unique=True, index=True, nullable=False)
|
||
job_type = Column(String, index=True, nullable=False)
|
||
status = Column(String, index=True, nullable=False, default="READY")
|
||
priority = Column(Integer, default=0)
|
||
payload = Column(JSON, nullable=True)
|
||
|
||
attempts = Column(Integer, default=0)
|
||
max_attempts = Column(Integer, default=3)
|
||
next_run_at = Column(DateTime, server_default=func.now())
|
||
|
||
locked_by = Column(String, nullable=True)
|
||
locked_at = Column(DateTime, nullable=True)
|
||
heartbeat_at = Column(DateTime, nullable=True)
|
||
|
||
workflow_run_id = Column(String, ForeignKey('workflow_runs.run_id'), nullable=True, index=True)
|
||
workflow_step_id = Column(String, nullable=True, index=True)
|
||
task_id = Column(String, nullable=True, index=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
started_at = Column(DateTime, nullable=True)
|
||
finished_at = Column(DateTime, nullable=True)
|
||
last_error = Column(Text, nullable=True)
|
||
|
||
__table_args__ = (
|
||
Index('ix_system_jobs_status_next', 'status', 'next_run_at'),
|
||
)
|
||
|
||
|
||
class ScanStateORM(Base):
|
||
__tablename__ = "scan_states"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
data_type = Column(String, index=True, nullable=False)
|
||
root_path = Column(String, index=True, nullable=False)
|
||
last_scan_mtime = Column(Float, default=0)
|
||
last_scan_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint('data_type', 'root_path', name='uq_scan_state'),
|
||
)
|
||
|
||
|
||
class ManagedRootORM(Base):
|
||
__tablename__ = "managed_roots"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
root_code = Column(String(96), unique=True, index=True, nullable=False)
|
||
root_role = Column(String(48), index=True, nullable=False)
|
||
display_name = Column(String(255), nullable=False)
|
||
path = Column(String, index=True, nullable=False)
|
||
path_kind = Column(String(24), nullable=False, default="windows")
|
||
source_kind = Column(String(24), index=True, nullable=False, default="env")
|
||
source_ref = Column(String(128), nullable=True)
|
||
scan_mode = Column(String(32), nullable=False, default="directory_walk")
|
||
owner_engine = Column(String(32), nullable=True)
|
||
enabled = Column(Boolean, nullable=False, default=True)
|
||
exists_flag = Column(Boolean, nullable=False, default=False)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
scan_cursors = relationship(
|
||
"ScanCursorORM",
|
||
back_populates="root",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
inventory_items = relationship(
|
||
"PathInventoryORM",
|
||
back_populates="root",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
asset_inventory_states = relationship(
|
||
"AssetInventoryStateORM",
|
||
back_populates="root",
|
||
cascade="all, delete-orphan",
|
||
)
|
||
|
||
__table_args__ = (
|
||
Index("idx_managed_roots_role_enabled", "root_role", "enabled"),
|
||
)
|
||
|
||
|
||
class ScanCursorORM(Base):
|
||
__tablename__ = "scan_cursors"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
root_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("managed_roots.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
cursor_key = Column(String(64), nullable=False, default="default")
|
||
cursor_type = Column(String(32), index=True, nullable=False, default="directory_walk")
|
||
scan_scope = Column(String(32), nullable=False, default="root")
|
||
status = Column(String(16), index=True, nullable=False, default="IDLE")
|
||
last_scan_started_at = Column(DateTime, nullable=True)
|
||
last_scan_finished_at = Column(DateTime, nullable=True)
|
||
last_seen_mtime = Column(Float, nullable=True)
|
||
last_seen_entry_count = Column(Integer, nullable=True)
|
||
last_seen_fingerprint = Column(String(64), nullable=True)
|
||
last_error = Column(Text, nullable=True)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
root = relationship("ManagedRootORM", back_populates="scan_cursors")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("root_ref_id", "cursor_key", name="uq_scan_cursor_root_key"),
|
||
)
|
||
|
||
|
||
class PathInventoryORM(Base):
|
||
__tablename__ = "path_inventory"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
root_ref_id = Column(
|
||
Integer,
|
||
ForeignKey("managed_roots.id", ondelete="CASCADE"),
|
||
index=True,
|
||
nullable=False,
|
||
)
|
||
relative_path = Column(String, nullable=False)
|
||
path_type = Column(String(24), index=True, nullable=False, default="file")
|
||
basename = Column(String(255), nullable=False)
|
||
extension = Column(String(32), nullable=True)
|
||
size_bytes = Column(BigInteger, nullable=True)
|
||
mtime = Column(Float, nullable=True)
|
||
ctime = Column(Float, nullable=True)
|
||
fingerprint = Column(String(64), nullable=True, index=True)
|
||
status = Column(String(16), index=True, nullable=False, default="DISCOVERED")
|
||
metadata_json = Column(JSON, nullable=True)
|
||
first_seen_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
last_seen_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
last_parsed_at = Column(DateTime, nullable=True)
|
||
|
||
root = relationship("ManagedRootORM", back_populates="inventory_items")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("root_ref_id", "relative_path", name="uq_path_inventory_root_relpath"),
|
||
Index("idx_path_inventory_root_status", "root_ref_id", "status"),
|
||
)
|
||
|
||
|
||
class SourceProductAssetORM(Base):
|
||
__tablename__ = "source_product_assets"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
asset_uid = Column(String(128), unique=True, index=True, nullable=False)
|
||
logical_product_uid = Column(String(128), index=True, nullable=True)
|
||
satellite_family = Column(String(32), index=True, nullable=True)
|
||
satellite = Column(String(32), index=True, nullable=True)
|
||
source_format = Column(String(32), index=True, nullable=False)
|
||
product_type = Column(String(64), nullable=True)
|
||
product_level = Column(String(64), nullable=True)
|
||
imaging_mode = Column(String(64), nullable=True)
|
||
polarization = Column(String(64), nullable=True)
|
||
absolute_orbit = Column(String(64), index=True, nullable=True)
|
||
relative_orbit = Column(String(64), index=True, nullable=True)
|
||
orbit_direction = Column(String(32), index=True, nullable=True)
|
||
acquisition_start_time_utc = Column(DateTime, index=True, nullable=True)
|
||
acquisition_stop_time_utc = Column(DateTime, nullable=True)
|
||
imaging_date = Column(String(8), index=True, nullable=True)
|
||
root_ref_id = Column(Integer, ForeignKey("managed_roots.id", ondelete="SET NULL"), index=True, nullable=True)
|
||
root_path = Column(String, nullable=True)
|
||
file_path = Column(String, unique=True, index=True, nullable=False)
|
||
archive_path = Column(String, nullable=True)
|
||
path_kind = Column(String(24), nullable=True)
|
||
file_name = Column(String(255), nullable=True)
|
||
file_stem = Column(String(255), nullable=True)
|
||
file_ext = Column(String(32), nullable=True)
|
||
size_bytes = Column(BigInteger, nullable=True)
|
||
mtime_epoch = Column(Float, nullable=True)
|
||
checksum_sha256 = Column(String(64), nullable=True)
|
||
checksum_status = Column(String(32), nullable=False, default="NOT_COMPUTED", server_default="NOT_COMPUTED")
|
||
archive_integrity_status = Column(String(32), nullable=False, default="NOT_CHECKED", server_default="NOT_CHECKED", index=True)
|
||
archive_integrity_method = Column(String(64), nullable=True)
|
||
archive_integrity_checked_at = Column(DateTime, nullable=True)
|
||
archive_integrity_error = Column(Text, nullable=True)
|
||
archive_integrity_version = Column(String(32), nullable=True)
|
||
archive_integrity_member_count = Column(Integer, nullable=True)
|
||
parser_name = Column(String(64), nullable=True)
|
||
parser_version = Column(String(32), nullable=True)
|
||
parse_status = Column(String(32), nullable=False, default="PENDING", server_default="PENDING", index=True)
|
||
parse_error = Column(Text, nullable=True)
|
||
parsed_at = Column(DateTime, nullable=True)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
is_active = Column(Boolean, nullable=False, default=True, server_default="true", index=True)
|
||
missing_since = Column(DateTime, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
root = relationship("ManagedRootORM")
|
||
|
||
__table_args__ = (
|
||
Index("idx_source_product_assets_family_date", "satellite_family", "imaging_date"),
|
||
Index("idx_source_product_assets_logical_product", "logical_product_uid"),
|
||
Index("idx_source_product_assets_root_active", "root_ref_id", "is_active"),
|
||
)
|
||
|
||
|
||
class SourceMetadataDocumentORM(Base):
|
||
__tablename__ = "source_metadata_documents"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
source_asset_id = Column(Integer, ForeignKey("source_product_assets.id", ondelete="CASCADE"), index=True, nullable=False)
|
||
radar_data_id = Column(Integer, ForeignKey("radar_data.id", ondelete="SET NULL"), index=True, nullable=True)
|
||
satellite_family = Column(String(32), index=True, nullable=True)
|
||
source_format = Column(String(32), index=True, nullable=True)
|
||
document_type = Column(String(32), index=True, nullable=False)
|
||
member_path = Column(String, nullable=False)
|
||
content_sha256 = Column(String(64), index=True, nullable=False)
|
||
content_encoding = Column(String(16), nullable=False, default="gzip", server_default="gzip")
|
||
content_bytes = Column(LargeBinary, nullable=False)
|
||
content_size_bytes = Column(BigInteger, nullable=True)
|
||
archive_path = Column(String, nullable=True)
|
||
archive_mtime = Column(Float, nullable=True)
|
||
parser_version = Column(String(32), nullable=True)
|
||
parse_status = Column(String(32), nullable=False, default="OK", server_default="OK", index=True)
|
||
parse_error = Column(Text, nullable=True)
|
||
extracted_at = Column(DateTime, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
source_asset = relationship("SourceProductAssetORM")
|
||
radar_data = relationship("RadarDataORM")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("source_asset_id", "document_type", "member_path", name="uq_source_metadata_document_member"),
|
||
Index("idx_source_metadata_documents_asset_type", "source_asset_id", "document_type"),
|
||
Index("idx_source_metadata_documents_radar_type", "radar_data_id", "document_type"),
|
||
)
|
||
|
||
|
||
class SARSceneGeometryProfileORM(Base):
|
||
__tablename__ = "sar_scene_geometry_profiles"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
source_asset_id = Column(Integer, ForeignKey("source_product_assets.id", ondelete="CASCADE"), unique=True, index=True, nullable=False)
|
||
radar_data_id = Column(Integer, ForeignKey("radar_data.id", ondelete="CASCADE"), unique=True, index=True, nullable=True)
|
||
satellite_family = Column(String(32), index=True, nullable=True)
|
||
satellite = Column(String(32), index=True, nullable=True)
|
||
source_format = Column(String(32), index=True, nullable=True)
|
||
imaging_mode = Column(String(64), index=True, nullable=True)
|
||
polarization = Column(String(64), index=True, nullable=True)
|
||
orbit_direction = Column(String(32), index=True, nullable=True)
|
||
look_direction = Column(String(32), index=True, nullable=True)
|
||
absolute_orbit = Column(String(64), index=True, nullable=True)
|
||
relative_orbit = Column(String(64), index=True, nullable=True)
|
||
acquisition_start_time_utc = Column(DateTime, index=True, nullable=True)
|
||
acquisition_stop_time_utc = Column(DateTime, nullable=True)
|
||
scene_center_lon = Column(Float, nullable=True)
|
||
scene_center_lat = Column(Float, nullable=True)
|
||
footprint_geom = Column(Geometry("POLYGON", srid=4326), index=True, nullable=True)
|
||
footprint_polygon = Column(JSON, nullable=True)
|
||
swath_summary_json = Column(JSON, nullable=True)
|
||
burst_summary_json = Column(JSON, nullable=True)
|
||
incidence_angle_min = Column(Float, nullable=True)
|
||
incidence_angle_max = Column(Float, nullable=True)
|
||
doppler_summary_json = Column(JSON, nullable=True)
|
||
state_vector_summary_json = Column(JSON, nullable=True)
|
||
metadata_quality = Column(String(32), nullable=False, default="UNKNOWN", server_default="UNKNOWN", index=True)
|
||
production_readiness = Column(String(32), nullable=False, default="UNKNOWN", server_default="UNKNOWN", index=True)
|
||
readiness_reasons_json = Column(JSON, nullable=True)
|
||
parser_version = Column(String(32), nullable=True)
|
||
parsed_at = Column(DateTime, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
source_asset = relationship("SourceProductAssetORM")
|
||
radar_data = relationship("RadarDataORM")
|
||
|
||
__table_args__ = (
|
||
Index("idx_sar_scene_geometry_profiles_family_date", "satellite_family", "acquisition_start_time_utc"),
|
||
Index("idx_sar_scene_geometry_profiles_track", "satellite_family", "relative_orbit", "orbit_direction"),
|
||
Index("idx_sar_scene_geometry_profiles_readiness", "production_readiness", "metadata_quality"),
|
||
)
|
||
|
||
|
||
class OrbitAssetORM(Base):
|
||
__tablename__ = "orbit_assets"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
orbit_uid = Column(String(128), unique=True, index=True, nullable=False)
|
||
satellite_family = Column(String(32), index=True, nullable=True)
|
||
satellite = Column(String(32), index=True, nullable=True)
|
||
orbit_type = Column(String(64), index=True, nullable=False)
|
||
native_format = Column(String(32), index=True, nullable=False)
|
||
quality_class = Column(String(32), index=True, nullable=False, default="unknown", server_default="unknown")
|
||
root_ref_id = Column(Integer, ForeignKey("managed_roots.id", ondelete="SET NULL"), index=True, nullable=True)
|
||
root_path = Column(String, nullable=True)
|
||
file_path = Column(String, unique=True, index=True, nullable=False)
|
||
file_name = Column(String(255), nullable=True)
|
||
file_stem = Column(String(255), nullable=True)
|
||
file_ext = Column(String(32), nullable=True)
|
||
size_bytes = Column(BigInteger, nullable=True)
|
||
mtime_epoch = Column(Float, nullable=True)
|
||
checksum_sha256 = Column(String(64), nullable=True)
|
||
checksum_status = Column(String(32), nullable=False, default="NOT_COMPUTED", server_default="NOT_COMPUTED")
|
||
validity_start_time_utc = Column(DateTime, index=True, nullable=True)
|
||
validity_stop_time_utc = Column(DateTime, index=True, nullable=True)
|
||
generation_time_utc = Column(DateTime, nullable=True)
|
||
published_time_utc = Column(DateTime, nullable=True)
|
||
parser_name = Column(String(64), nullable=True)
|
||
parser_version = Column(String(32), nullable=True)
|
||
parse_status = Column(String(32), nullable=False, default="PENDING", server_default="PENDING", index=True)
|
||
parse_error = Column(Text, nullable=True)
|
||
parsed_at = Column(DateTime, nullable=True)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
is_active = Column(Boolean, nullable=False, default=True, server_default="true", index=True)
|
||
missing_since = Column(DateTime, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
root = relationship("ManagedRootORM")
|
||
|
||
__table_args__ = (
|
||
Index("idx_orbit_assets_family_sat_window", "satellite_family", "satellite", "validity_start_time_utc", "validity_stop_time_utc"),
|
||
Index("idx_orbit_assets_root_active", "root_ref_id", "is_active"),
|
||
)
|
||
|
||
|
||
class SceneOrbitBindingORM(Base):
|
||
__tablename__ = "scene_orbit_bindings"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
radar_data_id = Column(Integer, ForeignKey("radar_data.id", ondelete="CASCADE"), index=True, nullable=False)
|
||
orbit_asset_id = Column(Integer, ForeignKey("orbit_assets.id", ondelete="CASCADE"), index=True, nullable=False)
|
||
binding_role = Column(String(32), nullable=False, default="primary_orbit", server_default="primary_orbit")
|
||
match_status = Column(String(32), nullable=False, default="CANDIDATE", server_default="CANDIDATE", index=True)
|
||
selection_status = Column(String(32), nullable=False, default="CANDIDATE", server_default="CANDIDATE", index=True)
|
||
selection_rank = Column(Integer, nullable=True)
|
||
priority_score = Column(Float, nullable=True)
|
||
coverage_margin_before_seconds = Column(Float, nullable=True)
|
||
coverage_margin_after_seconds = Column(Float, nullable=True)
|
||
match_rule_version = Column(String(64), nullable=True)
|
||
match_reason = Column(Text, nullable=True)
|
||
selected_at = Column(DateTime, nullable=True)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
radar_data = relationship("RadarDataORM")
|
||
orbit_asset = relationship("OrbitAssetORM")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("radar_data_id", "orbit_asset_id", "binding_role", name="uq_scene_orbit_binding_role"),
|
||
Index("idx_scene_orbit_bindings_scene_selected", "radar_data_id", "selection_status"),
|
||
)
|
||
|
||
|
||
class OrbitAssetDerivativeORM(Base):
|
||
__tablename__ = "orbit_asset_derivatives"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
orbit_asset_id = Column(Integer, ForeignKey("orbit_assets.id", ondelete="CASCADE"), index=True, nullable=False)
|
||
engine_code = Column(String(32), index=True, nullable=False)
|
||
derivative_format = Column(String(32), nullable=False)
|
||
derivative_role = Column(String(64), nullable=True)
|
||
pool_path = Column(String, index=True, nullable=False)
|
||
size_bytes = Column(BigInteger, nullable=True)
|
||
mtime_epoch = Column(Float, nullable=True)
|
||
checksum_sha256 = Column(String(64), nullable=True)
|
||
generation_status = Column(String(32), nullable=False, default="PENDING", server_default="PENDING", index=True)
|
||
generation_error = Column(Text, nullable=True)
|
||
generated_at = Column(DateTime, nullable=True)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
orbit_asset = relationship("OrbitAssetORM")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("orbit_asset_id", "engine_code", "derivative_format", "pool_path", name="uq_orbit_asset_derivative_pool_path"),
|
||
Index("idx_orbit_asset_derivatives_asset_engine", "orbit_asset_id", "engine_code"),
|
||
)
|
||
|
||
|
||
class AssetInventoryStateORM(Base):
|
||
__tablename__ = "asset_inventory_states"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
root_ref_id = Column(Integer, ForeignKey("managed_roots.id", ondelete="CASCADE"), index=True, nullable=False)
|
||
inventory_type = Column(String(32), index=True, nullable=False)
|
||
root_path = Column(String, nullable=False)
|
||
scan_mode = Column(String(32), nullable=False, default="file_pool", server_default="file_pool")
|
||
status = Column(String(32), nullable=False, default="NEVER_SCANNED", server_default="NEVER_SCANNED", index=True)
|
||
last_scan_started_at = Column(DateTime, nullable=True)
|
||
last_scan_finished_at = Column(DateTime, nullable=True)
|
||
last_seen_entry_count = Column(Integer, nullable=True)
|
||
last_asset_count = Column(Integer, nullable=True)
|
||
last_issue_count = Column(Integer, nullable=True)
|
||
parser_version = Column(String(32), nullable=True)
|
||
needs_rescan = Column(Boolean, nullable=False, default=True, server_default="true", index=True)
|
||
last_error = Column(Text, nullable=True)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
root = relationship("ManagedRootORM", back_populates="asset_inventory_states")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("root_ref_id", "inventory_type", name="uq_asset_inventory_state_root_type"),
|
||
Index("idx_asset_inventory_states_type_status", "inventory_type", "status"),
|
||
)
|
||
|
||
|
||
class AssetInventoryIssueORM(Base):
|
||
__tablename__ = "asset_inventory_issues"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
root_ref_id = Column(Integer, ForeignKey("managed_roots.id", ondelete="SET NULL"), index=True, nullable=True)
|
||
inventory_type = Column(String(32), index=True, nullable=False)
|
||
asset_ref_id = Column(Integer, ForeignKey("source_product_assets.id", ondelete="SET NULL"), index=True, nullable=True)
|
||
radar_data_id = Column(Integer, ForeignKey("radar_data.id", ondelete="SET NULL"), index=True, nullable=True)
|
||
orbit_asset_id = Column(Integer, ForeignKey("orbit_assets.id", ondelete="SET NULL"), index=True, nullable=True)
|
||
severity = Column(String(16), nullable=False, default="warning", server_default="warning", index=True)
|
||
issue_code = Column(String(64), index=True, nullable=False)
|
||
issue_message = Column(Text, nullable=True)
|
||
source_path = Column(String, nullable=True)
|
||
status = Column(String(16), nullable=False, default="OPEN", server_default="OPEN", index=True)
|
||
first_seen_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
last_seen_at = Column(DateTime, server_default=func.now(), nullable=False)
|
||
resolved_at = Column(DateTime, nullable=True)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
|
||
root = relationship("ManagedRootORM")
|
||
source_asset = relationship("SourceProductAssetORM", foreign_keys=[asset_ref_id])
|
||
radar_data = relationship("RadarDataORM")
|
||
orbit_asset = relationship("OrbitAssetORM")
|
||
|
||
__table_args__ = (
|
||
Index("idx_asset_inventory_issues_open", "status", "severity"),
|
||
Index("idx_asset_inventory_issues_root_type", "root_ref_id", "inventory_type"),
|
||
)
|
||
|
||
|
||
class WorkflowDefORM(Base):
|
||
"""Workflow definition (DAG template)."""
|
||
__tablename__ = "workflow_defs"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
name = Column(String, index=True, nullable=False)
|
||
version = Column(String, default="v1", nullable=False)
|
||
description = Column(Text, nullable=True)
|
||
definition = Column(JSON, nullable=True)
|
||
is_active = Column(Boolean, default=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint('name', 'version', name='uq_workflow_def_name_version'),
|
||
)
|
||
|
||
|
||
class WorkflowRunORM(Base):
|
||
"""Workflow run instance."""
|
||
__tablename__ = "workflow_runs"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
run_id = Column(String, unique=True, index=True, nullable=False)
|
||
workflow_def_id = Column(Integer, ForeignKey('workflow_defs.id'), nullable=True)
|
||
workflow_name = Column(String, index=True, nullable=False)
|
||
status = Column(String, index=True, nullable=False, default="PENDING")
|
||
params = Column(JSON, nullable=True)
|
||
tags = Column(JSON, nullable=True)
|
||
created_by = Column(String, nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
started_at = Column(DateTime, nullable=True)
|
||
ended_at = Column(DateTime, nullable=True)
|
||
|
||
steps = relationship("WorkflowStepORM", back_populates="run", cascade="all, delete-orphan")
|
||
artifacts = relationship("WorkflowArtifactORM", back_populates="run", cascade="all, delete-orphan")
|
||
|
||
|
||
class WorkflowStepORM(Base):
|
||
"""Workflow step instance within a run."""
|
||
__tablename__ = "workflow_steps"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
run_id = Column(String, ForeignKey('workflow_runs.run_id'), index=True, nullable=False)
|
||
step_id = Column(String, nullable=False)
|
||
step_name = Column(String, index=True, nullable=False)
|
||
status = Column(String, index=True, nullable=False, default="PENDING")
|
||
depends_on = Column(JSON, nullable=True)
|
||
params = Column(JSON, nullable=True)
|
||
outputs = Column(JSON, nullable=True)
|
||
error = Column(Text, nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
started_at = Column(DateTime, nullable=True)
|
||
ended_at = Column(DateTime, nullable=True)
|
||
|
||
run = relationship("WorkflowRunORM", back_populates="steps")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint('run_id', 'step_id', name='uq_workflow_step_run_step'),
|
||
)
|
||
|
||
|
||
class WorkflowArtifactORM(Base):
|
||
"""Artifacts produced by workflow steps."""
|
||
__tablename__ = "workflow_artifacts"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
run_id = Column(String, ForeignKey('workflow_runs.run_id'), index=True, nullable=False)
|
||
step_id = Column(String, nullable=True, index=True)
|
||
artifact_type = Column(String, index=True, nullable=False)
|
||
path = Column(String, nullable=False)
|
||
metadata_json = Column("metadata", JSON, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
run = relationship("WorkflowRunORM", back_populates="artifacts")
|
||
|
||
|
||
class SystemWorkerHeartbeatORM(Base):
|
||
"""Worker heartbeat for ops health checks."""
|
||
__tablename__ = "system_worker_heartbeats"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
worker_id = Column(String, unique=True, index=True, nullable=False)
|
||
hostname = Column(String, nullable=True)
|
||
pid = Column(Integer, nullable=True)
|
||
note = Column(String, nullable=True)
|
||
started_at = Column(DateTime, server_default=func.now())
|
||
last_seen = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class DinsarTaskBatchORM(Base):
|
||
__tablename__ = "dinsar_task_batches"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
batch_id = Column(String, unique=True, index=True, nullable=False)
|
||
name = Column(String, nullable=True)
|
||
status = Column(String, index=True, nullable=False, default="PENDING")
|
||
total_items = Column(Integer, default=0)
|
||
completed_items = Column(Integer, default=0)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
items = relationship("DinsarTaskItemORM", back_populates="batch", cascade="all, delete-orphan")
|
||
|
||
|
||
class DinsarTaskItemORM(Base):
|
||
__tablename__ = "dinsar_task_items"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
batch_id = Column(String, ForeignKey("dinsar_task_batches.batch_id"), index=True, nullable=False)
|
||
|
||
task_name = Column(String, nullable=True)
|
||
task_alias = Column(String, index=True, nullable=True)
|
||
pair_key = Column(String(128), index=True, nullable=True)
|
||
scene_pair_uid = Column(String(64), index=True, nullable=True)
|
||
network_run_id = Column(String(64), index=True, nullable=True)
|
||
network_edge_id = Column(Integer, nullable=True)
|
||
policy_version = Column(String(32), index=True, nullable=True)
|
||
selection_strategy = Column(String(32), index=True, nullable=True)
|
||
master_path = Column(String, nullable=False)
|
||
slave_path = Column(String, nullable=False)
|
||
master_satellite = Column(String, nullable=True)
|
||
master_imaging_date = Column(String, nullable=True)
|
||
master_imaging_mode = Column(String, nullable=True)
|
||
master_polarization = Column(String, nullable=True)
|
||
slave_satellite = Column(String, nullable=True)
|
||
slave_imaging_date = Column(String, nullable=True)
|
||
slave_imaging_mode = Column(String, nullable=True)
|
||
slave_polarization = Column(String, nullable=True)
|
||
time_baseline_days = Column(Integer, nullable=True)
|
||
spatial_baseline_meters = Column(Float, nullable=True)
|
||
scene_center_distance_meters = Column(Float, nullable=True)
|
||
|
||
status = Column(String, index=True, nullable=False, default="PENDING")
|
||
remark = Column(Text, nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
batch = relationship("DinsarTaskBatchORM", back_populates="items")
|
||
|
||
|
||
class DinsarProductionRunORM(Base):
|
||
__tablename__ = "dinsar_production_runs"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
run_id = Column(String(64), unique=True, index=True, nullable=False)
|
||
task_id = Column(String, index=True, nullable=True)
|
||
workflow_run_id = Column(String, index=True, nullable=True)
|
||
|
||
product_family = Column(String(32), index=True, nullable=True)
|
||
engine_code = Column(String(32), index=True, nullable=False, default="sarscape")
|
||
profile_code = Column(String(64), index=True, nullable=False, default="custom6")
|
||
mode = Column(String(32), index=True, nullable=False, default="custom")
|
||
source_root = Column(String, nullable=False)
|
||
publish_root_dir = Column(String, nullable=True)
|
||
status = Column(String(32), index=True, nullable=False, default="PENDING")
|
||
cancel_requested = Column(Boolean, nullable=False, default=False)
|
||
|
||
total_items = Column(Integer, nullable=False, default=0)
|
||
completed_items = Column(Integer, nullable=False, default=0)
|
||
failed_items = Column(Integer, nullable=False, default=0)
|
||
skipped_items = Column(Integer, nullable=False, default=0)
|
||
|
||
latest_message = Column(Text, nullable=True)
|
||
params_json = Column(JSON, nullable=True)
|
||
summary_json = Column(JSON, nullable=True)
|
||
created_by = Column(String(128), nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
started_at = Column(DateTime, nullable=True)
|
||
ended_at = Column(DateTime, nullable=True)
|
||
|
||
items = relationship("DinsarProductionRunItemORM", back_populates="run", cascade="all, delete-orphan")
|
||
|
||
|
||
class DinsarProductionRunItemORM(Base):
|
||
__tablename__ = "dinsar_production_run_items"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
run_id = Column(String(64), ForeignKey("dinsar_production_runs.run_id"), index=True, nullable=False)
|
||
|
||
order_index = Column(Integer, nullable=False, default=0)
|
||
task_name = Column(String(255), nullable=True)
|
||
task_alias = Column(String(255), index=True, nullable=True)
|
||
pair_key = Column(String(128), index=True, nullable=True)
|
||
pair_uid = Column(String(64), index=True, nullable=True)
|
||
network_run_id = Column(String(64), index=True, nullable=True)
|
||
network_edge_id = Column(Integer, nullable=True)
|
||
policy_version = Column(String(32), index=True, nullable=True)
|
||
selection_strategy = Column(String(32), index=True, nullable=True)
|
||
|
||
source_task_dir = Column(String, nullable=False)
|
||
results_root_dir = Column(String, nullable=False)
|
||
status = Column(String(32), index=True, nullable=False, default="PENDING")
|
||
current_step = Column(String(64), nullable=True)
|
||
attempt_count = Column(Integer, nullable=False, default=0)
|
||
latest_run_key = Column(String(128), index=True, nullable=True)
|
||
latest_output_dir = Column(String, nullable=True)
|
||
latest_manifest_path = Column(String, nullable=True)
|
||
latest_log_path = Column(String, nullable=True)
|
||
last_error = Column(Text, nullable=True)
|
||
metrics_json = Column(JSON, nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
started_at = Column(DateTime, nullable=True)
|
||
ended_at = Column(DateTime, nullable=True)
|
||
|
||
run = relationship("DinsarProductionRunORM", back_populates="items")
|
||
executions = relationship("DinsarProductionExecutionORM", back_populates="item", cascade="all, delete-orphan")
|
||
|
||
__table_args__ = (
|
||
Index("idx_dinsar_run_items_run_order", "run_id", "order_index"),
|
||
)
|
||
|
||
|
||
class DinsarProductionExecutionORM(Base):
|
||
__tablename__ = "dinsar_production_executions"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
execution_id = Column(String(128), unique=True, index=True, nullable=False)
|
||
run_id = Column(String(64), ForeignKey("dinsar_production_runs.run_id"), index=True, nullable=False)
|
||
item_id = Column(Integer, ForeignKey("dinsar_production_run_items.id"), index=True, nullable=False)
|
||
|
||
run_key = Column(String(128), index=True, nullable=False)
|
||
status = Column(String(32), index=True, nullable=False, default="PENDING")
|
||
output_dir = Column(String, nullable=False)
|
||
manifest_path = Column(String, nullable=True)
|
||
log_path = Column(String, nullable=True)
|
||
subprocess_pid = Column(Integer, nullable=True)
|
||
error_message = Column(Text, nullable=True)
|
||
metrics_json = Column(JSON, nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
started_at = Column(DateTime, nullable=True)
|
||
ended_at = Column(DateTime, nullable=True)
|
||
|
||
item = relationship("DinsarProductionRunItemORM", back_populates="executions")
|
||
|
||
__table_args__ = (
|
||
Index("idx_dinsar_exec_run_item", "run_id", "item_id"),
|
||
)
|
||
|
||
|
||
class PsTaskBatchORM(Base):
|
||
__tablename__ = "ps_task_batches"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
batch_id = Column(String, unique=True, index=True, nullable=False)
|
||
name = Column(String, nullable=True)
|
||
direction = Column(String, nullable=True)
|
||
plan_id = Column(String(64), index=True, nullable=True)
|
||
plan_strategy = Column(String(32), nullable=True)
|
||
status = Column(String, index=True, nullable=False, default="PENDING")
|
||
total_items = Column(Integer, default=0)
|
||
completed_items = Column(Integer, default=0)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
items = relationship("PsTaskItemORM", back_populates="batch", cascade="all, delete-orphan")
|
||
|
||
|
||
class PsTaskItemORM(Base):
|
||
__tablename__ = "ps_task_items"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
batch_id = Column(String, ForeignKey("ps_task_batches.batch_id"), index=True, nullable=False)
|
||
plan_item_ref_id = Column(Integer, index=True, nullable=True)
|
||
|
||
file_path = Column(String, nullable=False)
|
||
satellite = Column(String, nullable=True)
|
||
imaging_date = Column(String, nullable=True)
|
||
polarization = Column(String, nullable=True)
|
||
has_orbit_data = Column(Boolean, default=False)
|
||
|
||
status = Column(String, index=True, nullable=False, default="PENDING")
|
||
remark = Column(Text, nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
batch = relationship("PsTaskBatchORM", back_populates="items")
|
||
|
||
|
||
class PsTimeseriesRunORM(Base):
|
||
__tablename__ = "ps_timeseries_runs"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
run_id = Column(String(64), unique=True, index=True, nullable=False)
|
||
batch_id = Column(String, ForeignKey("ps_task_batches.batch_id"), index=True, nullable=False)
|
||
plan_id = Column(String(64), index=True, nullable=True)
|
||
plan_strategy = Column(String(32), nullable=True)
|
||
|
||
product_family = Column(String(32), index=True, nullable=True)
|
||
run_name = Column(String(255), nullable=False)
|
||
catalog_name = Column(String(32), index=True, nullable=False, default="psinsar")
|
||
stack_key = Column(String(128), index=True, nullable=True)
|
||
mode = Column(String(32), nullable=False, default="sbas")
|
||
engine_code = Column(String(32), index=True, nullable=False, default="isce2")
|
||
processor_code = Column(String(64), nullable=False, default="isce2_stack_mintpy")
|
||
runtime_id = Column(String(64), nullable=True)
|
||
env_name = Column(String(128), nullable=True)
|
||
wsl_distro = Column(String(128), nullable=True)
|
||
|
||
status = Column(String(32), index=True, nullable=False, default="PENDING")
|
||
task_id = Column(String, index=True, nullable=True)
|
||
workflow_run_id = Column(String, index=True, nullable=True)
|
||
|
||
direction = Column(String(64), nullable=True)
|
||
stack_size = Column(Integer, nullable=False, default=0)
|
||
reference_date = Column(String(8), index=True, nullable=True)
|
||
water_mask_mode = Column(String(64), nullable=True)
|
||
|
||
dem_path_windows = Column(String, nullable=True)
|
||
dem_path_wsl = Column(String, nullable=True)
|
||
orbit_pool_windows = Column(String, nullable=True)
|
||
orbit_pool_wsl = Column(String, nullable=True)
|
||
|
||
work_root_windows = Column(String, nullable=True)
|
||
work_root_wsl = Column(String, nullable=True)
|
||
publish_dir_windows = Column(String, nullable=True)
|
||
publish_dir_wsl = Column(String, nullable=True)
|
||
manifest_path_windows = Column(String, nullable=True)
|
||
manifest_path_wsl = Column(String, nullable=True)
|
||
|
||
params_json = Column(JSON, nullable=True)
|
||
summary_json = Column(JSON, nullable=True)
|
||
input_snapshot_json = Column(JSON, nullable=True)
|
||
orbit_summary_json = Column(JSON, nullable=True)
|
||
quality_summary_json = Column(JSON, nullable=True)
|
||
|
||
error_message = Column(Text, nullable=True)
|
||
created_by = Column(String(128), nullable=True)
|
||
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
started_at = Column(DateTime, nullable=True)
|
||
ended_at = Column(DateTime, nullable=True)
|
||
|
||
batch = relationship("PsTaskBatchORM")
|
||
|
||
__table_args__ = (
|
||
Index("idx_ps_timeseries_runs_batch_status", "batch_id", "status"),
|
||
Index("idx_ps_timeseries_runs_catalog_created", "catalog_name", "created_at"),
|
||
)
|
||
|
||
|
||
class AuthUserORM(Base):
|
||
__tablename__ = "auth_users"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
username = Column(String, unique=True, index=True, nullable=False)
|
||
password_hash = Column(Text, nullable=False)
|
||
role = Column(String, index=True, nullable=False, default="viewer")
|
||
is_active = Column(Boolean, nullable=False, default=True)
|
||
created_by = Column(String, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
last_login_at = Column(DateTime, nullable=True)
|
||
|
||
sessions = relationship("AuthSessionORM", back_populates="user", cascade="all, delete-orphan")
|
||
|
||
|
||
class AuthSessionORM(Base):
|
||
__tablename__ = "auth_sessions"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
token_hash = Column(String, unique=True, index=True, nullable=False)
|
||
user_id = Column(Integer, ForeignKey("auth_users.id"), nullable=False, index=True)
|
||
expires_at = Column(DateTime, nullable=False, index=True)
|
||
is_revoked = Column(Boolean, nullable=False, default=False, index=True)
|
||
ip_address = Column(String, nullable=True)
|
||
user_agent = Column(String, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
last_seen_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
user = relationship("AuthUserORM", back_populates="sessions")
|
||
|
||
|
||
class AuthAuditLogORM(Base):
|
||
__tablename__ = "auth_audit_logs"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
user_id = Column(Integer, ForeignKey("auth_users.id"), nullable=True, index=True)
|
||
username = Column(String, nullable=True)
|
||
action = Column(String, nullable=False, index=True)
|
||
resource = Column(String, nullable=True, index=True)
|
||
detail = Column(JSON, nullable=True)
|
||
ip_address = Column(String, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now(), index=True)
|
||
|
||
|
||
class AuthRateLimitORM(Base):
|
||
"""
|
||
登录限流状态持久化表。
|
||
每个 throttle_key(用户名+IP 组合)对应一行,记录失败时间戳列表和锁定截止时间。
|
||
"""
|
||
__tablename__ = "auth_rate_limits"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
throttle_key = Column(String, unique=True, index=True, nullable=False)
|
||
failure_timestamps = Column(JSON, nullable=False, default=list)
|
||
locked_until = Column(DateTime, nullable=True)
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Water body monitoring v2 (SARscape-based)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
class SARSceneGeoORM(Base):
|
||
"""单景 SAR 影像地理编码结果(多视 + 地理编码 + 辐射定标)。"""
|
||
__tablename__ = "sar_scene_geo"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
radar_data_id = Column(Integer, ForeignKey("radar_data.id"), index=True, nullable=False)
|
||
geo_path = Column(String, nullable=True) # 地理编码 dB 文件路径(ENVI 格式,无扩展名)
|
||
pixel_size_m = Column(Float, nullable=True) # 输出像素大小(m)
|
||
analysis_tif_path = Column(String, nullable=True, index=True)
|
||
analysis_dir = Column(String, nullable=True)
|
||
analysis_preview_path = Column(String, nullable=True)
|
||
analysis_engine = Column(String(32), nullable=True, index=True)
|
||
analysis_profile = Column(String(64), nullable=True, index=True)
|
||
analysis_backscatter_unit = Column(String(32), nullable=True)
|
||
analysis_nodata_value = Column(Float, nullable=True)
|
||
analysis_metadata_json = Column(JSON, nullable=True)
|
||
analysis_quality_json = Column(JSON, nullable=True)
|
||
status = Column(String, nullable=False, default="PENDING", index=True)
|
||
error_msg = Column(Text, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
radar_data = relationship("RadarDataORM", foreign_keys=[radar_data_id])
|
||
pre_flood_detections = relationship(
|
||
"FloodDetectionORM", foreign_keys="FloodDetectionORM.pre_scene_id", back_populates="pre_scene"
|
||
)
|
||
post_flood_detections = relationship(
|
||
"FloodDetectionORM", foreign_keys="FloodDetectionORM.post_scene_id", back_populates="post_scene"
|
||
)
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("radar_data_id", name="uq_sar_scene_geo_radar"),
|
||
)
|
||
|
||
|
||
class FloodDetectionORM(Base):
|
||
"""洪涝检测结果(灾前 + 灾后两景配对)。"""
|
||
__tablename__ = "flood_detections"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
pre_scene_id = Column(Integer, ForeignKey("sar_scene_geo.id"), index=True, nullable=False)
|
||
post_scene_id = Column(Integer, ForeignKey("sar_scene_geo.id"), index=True, nullable=False)
|
||
output_dir = Column(String, nullable=True) # 输出目录
|
||
classified_path = Column(String, nullable=True) # 分类图路径(ENVI 格式)
|
||
flood_area_km2 = Column(Float, nullable=True) # 洪涝面积(km²)
|
||
stable_water_area_km2 = Column(Float, nullable=True) # 稳定水体面积(km²)
|
||
status = Column(String, nullable=False, default="PENDING", index=True)
|
||
error_msg = Column(Text, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
pre_scene = relationship("SARSceneGeoORM", foreign_keys=[pre_scene_id], back_populates="pre_flood_detections")
|
||
post_scene = relationship("SARSceneGeoORM", foreign_keys=[post_scene_id], back_populates="post_flood_detections")
|
||
overlays = relationship("FloodOverlayORM", back_populates="detection")
|
||
products = relationship("FloodProductORM", back_populates="detection", foreign_keys="FloodProductORM.detection_id")
|
||
|
||
__table_args__ = (
|
||
UniqueConstraint("pre_scene_id", "post_scene_id", name="uq_flood_detection_pair"),
|
||
)
|
||
|
||
|
||
class WaterDetectionORM(Base):
|
||
"""水体检测结果(Otsu 自适应阈值 + DEM/坡度约束 + 形态学 + 连通分量过滤)。"""
|
||
__tablename__ = "water_detections"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
scene_id = Column(Integer, ForeignKey("sar_scene_geo.id"), nullable=True, index=True)
|
||
input_path = Column(String, nullable=True) # 输入 GeoTIFF 路径
|
||
output_path = Column(String, nullable=True) # 输出二值掩膜路径
|
||
water_area_km2 = Column(Float, nullable=True)
|
||
water_pixel_count = Column(Integer, nullable=True)
|
||
otsu_threshold_db = Column(Float, nullable=True)
|
||
status = Column(String, nullable=False, default="PENDING", index=True)
|
||
error_msg = Column(Text, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class WaterExtractionORM(Base):
|
||
"""Water extraction result used by the flood-analysis pipeline."""
|
||
__tablename__ = "water_extractions"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
scene_id = Column(Integer, ForeignKey("sar_scene_geo.id"), nullable=True, index=True)
|
||
processor = Column(String(32), nullable=False, default="otsu", index=True)
|
||
task_id = Column(String(64), nullable=True, index=True)
|
||
input_path = Column(String, nullable=True)
|
||
output_path = Column(String, nullable=True)
|
||
preview_path = Column(String, nullable=True)
|
||
vector_path = Column(String, nullable=True)
|
||
water_area_km2 = Column(Float, nullable=True)
|
||
water_pixel_count = Column(Integer, nullable=True)
|
||
threshold_value = Column(Float, nullable=True)
|
||
metadata_json = Column(JSON, nullable=True)
|
||
status = Column(String(16), nullable=False, default="PENDING", index=True)
|
||
error_msg = Column(Text, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
scene = relationship("SARSceneGeoORM", foreign_keys=[scene_id])
|
||
|
||
|
||
class FloodOverlayORM(Base):
|
||
"""Spatial overlay result for a flood detection."""
|
||
__tablename__ = "flood_overlays"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
detection_id = Column(Integer, ForeignKey("flood_detections.id"), nullable=False, index=True)
|
||
flood_vector_path = Column(String, nullable=True)
|
||
hazard_points_hit = Column(Integer, nullable=False, default=0)
|
||
hazard_points_near = Column(Integer, nullable=False, default=0)
|
||
hazard_points_total = Column(Integer, nullable=False, default=0)
|
||
dinsar_products_intersecting = Column(Integer, nullable=False, default=0)
|
||
affected_area_km2 = Column(Float, nullable=True)
|
||
summary_json = Column(JSON, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
detection = relationship("FloodDetectionORM", back_populates="overlays")
|
||
products = relationship("FloodProductORM", back_populates="overlay", foreign_keys="FloodProductORM.overlay_id")
|
||
|
||
|
||
class FloodProductORM(Base):
|
||
"""Published flood-analysis product package."""
|
||
__tablename__ = "flood_products"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
product_id = Column(String(64), unique=True, index=True, nullable=False)
|
||
detection_id = Column(Integer, ForeignKey("flood_detections.id"), nullable=True, index=True)
|
||
overlay_id = Column(Integer, ForeignKey("flood_overlays.id"), nullable=True, index=True)
|
||
display_name = Column(String(255), nullable=False)
|
||
status = Column(String(16), nullable=False, default="READY", index=True)
|
||
publish_dir = Column(String, unique=True, nullable=True)
|
||
manifest_path = Column(String, unique=True, nullable=True)
|
||
geom = Column(Geometry("POLYGON", srid=4326), nullable=True)
|
||
summary_json = Column(JSON, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
|
||
detection = relationship("FloodDetectionORM", back_populates="products", foreign_keys=[detection_id])
|
||
overlay = relationship("FloodOverlayORM", back_populates="products", foreign_keys=[overlay_id])
|
||
|
||
|
||
class GF3ProcessingORM(Base):
|
||
"""GF3 L1A→L2 处理记录(辐射定标 + RPC 几何校正)。"""
|
||
__tablename__ = "gf3_processing"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
input_dir = Column(String, nullable=False)
|
||
output_dir = Column(String, nullable=True)
|
||
polarizations = Column(String, nullable=True) # JSON 数组,如 '["VH","VV"]'
|
||
l2_paths = Column(String, nullable=True) # JSON 数组
|
||
resolution = Column(Float, default=0.0002)
|
||
status = Column(String, nullable=False, default="PENDING", index=True)
|
||
error_msg = Column(Text, nullable=True)
|
||
created_at = Column(DateTime, server_default=func.now())
|
||
updated_at = Column(DateTime, server_default=func.now(), onupdate=func.now())
|
||
|
||
|
||
class AiDiagnosisORM(Base):
|
||
"""AI 诊断记录表,存储所有 VLM 对 D-InSAR 结果的诊断报告。"""
|
||
__tablename__ = "ai_diagnosis"
|
||
|
||
id = Column(Integer, primary_key=True, autoincrement=True)
|
||
|
||
# 关联信息
|
||
result_id = Column(Integer, ForeignKey("dinsar_results.id", ondelete="CASCADE"), nullable=False, index=True)
|
||
product_ref_id = Column(Integer, ForeignKey("result_products.id", ondelete="SET NULL"), nullable=True, index=True)
|
||
product_id = Column(String(64), nullable=True, index=True)
|
||
task_id = Column(String(50), nullable=True, index=True) # 关联 system_tasks
|
||
|
||
# 模型与配置
|
||
model_name = Column(String(100), nullable=False)
|
||
prompt_template = Column(String(50), nullable=False) # 'quick'/'standard'/'detailed'/'custom'
|
||
prompt_text = Column(Text, nullable=True) # 实际使用的完整 prompt(审计用)
|
||
|
||
# 诊断结果
|
||
diagnosis_markdown = Column(Text, nullable=True) # Markdown 格式报告
|
||
risk_level = Column(String(20), nullable=True, index=True) # 'LOW'/'MEDIUM'/'HIGH'
|
||
confidence_score = Column(Float, nullable=True) # 0.0-1.0 模型自评置信度
|
||
|
||
# 上下文快照(避免关联查询)
|
||
result_name = Column(String(255), nullable=True)
|
||
date_range = Column(String(100), nullable=True) # 如 '20240101-20240115'
|
||
quality_score = Column(Float, nullable=True) # 当时的 ai_score
|
||
hazards_found = Column(Integer, default=0, nullable=False)
|
||
hazards_snapshot = Column(JSON, nullable=True) # [{name, type, location}]
|
||
|
||
# 元数据
|
||
created_at = Column(DateTime, server_default=func.now(), nullable=False, index=True)
|
||
duration_seconds = Column(Float, nullable=True) # 诊断耗时
|
||
error_message = Column(Text, nullable=True) # 失败时记录错误
|
||
|
||
# 关系
|
||
result = relationship("DinsarResultORM", backref="diagnoses")
|
||
product = relationship("ResultProductORM", foreign_keys=[product_ref_id])
|
||
|
||
__table_args__ = (
|
||
Index("idx_ai_diagnosis_created_at_desc", created_at.desc()),
|
||
)
|
||
|
||
|