Initial public release
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
GeoIntel release gates / Compile, test, contracts and builds (push) Successful in 1m49s
GeoIntel release gates / Python and npm vulnerability policy (push) Successful in 21s
GeoIntel release gates / Production AI image, SBOM and container scan (push) Successful in 5m39s
GeoIntel release gates / Deploy exact gated revision to Unraid (push) Failing after 58m43s
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
from .entities import (
|
||||
AoiOperation,
|
||||
AoiOperationPartition,
|
||||
AnalysisRun,
|
||||
Area,
|
||||
Dataset,
|
||||
DatasetLineageEdge,
|
||||
DatasetQuarantine,
|
||||
DatasetVersion,
|
||||
Detection,
|
||||
DetectionReview,
|
||||
Export,
|
||||
Job,
|
||||
Metric,
|
||||
Project,
|
||||
QualityCheck,
|
||||
Segmentation,
|
||||
SourceRegistry,
|
||||
SourceSnapshot,
|
||||
VectorFeature,
|
||||
)
|
||||
|
||||
__all__ = [
|
||||
"AnalysisRun",
|
||||
"AoiOperation",
|
||||
"AoiOperationPartition",
|
||||
"Area",
|
||||
"Dataset",
|
||||
"DatasetLineageEdge",
|
||||
"DatasetQuarantine",
|
||||
"DatasetVersion",
|
||||
"Detection",
|
||||
"DetectionReview",
|
||||
"Export",
|
||||
"Job",
|
||||
"Metric",
|
||||
"Project",
|
||||
"QualityCheck",
|
||||
"Segmentation",
|
||||
"SourceRegistry",
|
||||
"SourceSnapshot",
|
||||
"VectorFeature",
|
||||
]
|
||||
@@ -0,0 +1,788 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from geoalchemy2 import Geometry
|
||||
from sqlalchemy import CheckConstraint, DateTime, ForeignKey, Float, Index, JSON, String, Text, UniqueConstraint, func, text
|
||||
from sqlalchemy.sql.sqltypes import Integer
|
||||
from sqlalchemy.dialects.postgresql import UUID
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
|
||||
from app.db.base import Base
|
||||
|
||||
|
||||
SOURCE_CLASSIFICATIONS = (
|
||||
"authoritative",
|
||||
"corroborative",
|
||||
"contextual",
|
||||
"derived",
|
||||
"experimental",
|
||||
)
|
||||
SOURCE_FRESHNESS_STATUSES = (
|
||||
"unknown",
|
||||
"current",
|
||||
"due",
|
||||
"stale",
|
||||
"not_applicable",
|
||||
"review_required",
|
||||
)
|
||||
SOURCE_INGEST_STATUSES = (
|
||||
"registered",
|
||||
"configured",
|
||||
"not_configured",
|
||||
"available",
|
||||
"ingested",
|
||||
"failed",
|
||||
"quarantined",
|
||||
"legacy_unverified",
|
||||
)
|
||||
PROVENANCE_STATUSES = ("complete", "incomplete", "not_applicable")
|
||||
LINEAGE_STATUSES = ("complete", "incomplete", "not_applicable")
|
||||
VALIDATION_STATUSES = ("not_validated", "passed", "failed")
|
||||
QUARANTINE_STATUSES = ("not_quarantined", "quarantined")
|
||||
|
||||
|
||||
class Project(Base):
|
||||
__tablename__ = "projects"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
description: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
region: Mapped[str] = mapped_column(String(120), default="Belgium and Belgian North Sea")
|
||||
status: Mapped[str] = mapped_column(String(32), default="active")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
areas: Mapped[list["Area"]] = relationship("Area", back_populates="project", cascade="all, delete-orphan")
|
||||
datasets: Mapped[list["Dataset"]] = relationship("Dataset", back_populates="project", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class Area(Base):
|
||||
__tablename__ = "areas"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
geometry: Mapped[str] = mapped_column(Geometry("MultiPolygon", srid=4326), nullable=False)
|
||||
original_crs: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
area_m2: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
bbox: Mapped[str | None] = mapped_column(Geometry("Polygon", srid=4326), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
project: Mapped[Project] = relationship("Project", back_populates="areas")
|
||||
|
||||
|
||||
class SourceRegistry(Base):
|
||||
"""Server-owned source identity and authority contract.
|
||||
|
||||
Dataset metadata remains descriptive until a governed importer binds a
|
||||
dataset to both this registry entry and an immutable SourceSnapshot.
|
||||
"""
|
||||
|
||||
__tablename__ = "source_registry"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("source_key", name="uq_source_registry_source_key"),
|
||||
CheckConstraint(
|
||||
"classification IN ('authoritative', 'corroborative', 'contextual', 'derived', 'experimental')",
|
||||
name="ck_source_registry_classification",
|
||||
),
|
||||
CheckConstraint(
|
||||
"freshness_status IN ('unknown', 'current', 'due', 'stale', 'not_applicable', 'review_required')",
|
||||
name="ck_source_registry_freshness_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"ingest_status IN ('registered', 'configured', 'not_configured', 'available', 'ingested', "
|
||||
"'failed', 'quarantined', 'legacy_unverified')",
|
||||
name="ck_source_registry_ingest_status",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
source_key: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
display_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
classification: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||
authority_name: Mapped[str] = mapped_column(String(255), nullable=False, default="unknown", server_default="unknown")
|
||||
authority_scope_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
provider_adapter_key: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
source_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
license_name: Mapped[str] = mapped_column(String(255), nullable=False, default="unknown", server_default="unknown")
|
||||
license_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
usage_restrictions: Mapped[str] = mapped_column(Text, nullable=False, default="unknown", server_default="unknown")
|
||||
default_crs: Mapped[str] = mapped_column(String(64), nullable=False, default="unknown", server_default="unknown")
|
||||
default_units: Mapped[str] = mapped_column(String(120), nullable=False, default="unknown", server_default="unknown")
|
||||
spatial_resolution_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
temporal_coverage_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
geographic_coverage_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
expected_geometry_types_json: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
|
||||
expected_attributes_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
usage_policy_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
freshness_status: Mapped[str] = mapped_column(
|
||||
String(32), nullable=False, default="unknown", server_default="unknown"
|
||||
)
|
||||
ingest_status: Mapped[str] = mapped_column(
|
||||
String(32), nullable=False, default="registered", server_default="registered"
|
||||
)
|
||||
known_limitations_json: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
|
||||
registry_metadata_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
snapshots: Mapped[list["SourceSnapshot"]] = relationship(
|
||||
"SourceSnapshot", back_populates="source_registry", cascade="all, delete-orphan"
|
||||
)
|
||||
datasets: Mapped[list["Dataset"]] = relationship("Dataset", back_populates="source_registry")
|
||||
dataset_versions: Mapped[list["DatasetVersion"]] = relationship("DatasetVersion", back_populates="source_registry")
|
||||
|
||||
|
||||
class SourceSnapshot(Base):
|
||||
"""Immutable source-version evidence recorded by governed ingestion."""
|
||||
|
||||
__tablename__ = "source_snapshots"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("source_registry_id", "snapshot_key", name="uq_source_snapshots_registry_key"),
|
||||
CheckConstraint(
|
||||
"freshness_status IN ('unknown', 'current', 'due', 'stale', 'not_applicable', 'review_required')",
|
||||
name="ck_source_snapshots_freshness_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"ingest_status IN ('registered', 'configured', 'not_configured', 'available', 'ingested', "
|
||||
"'failed', 'quarantined', 'legacy_unverified')",
|
||||
name="ck_source_snapshots_ingest_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"checksum_sha256 = lower(checksum_sha256) AND checksum_sha256 ~ '^[0-9a-f]{64}$'",
|
||||
name="ck_source_snapshots_checksum_sha256",
|
||||
),
|
||||
Index("ix_source_snapshots_registry_fetched", "source_registry_id", "fetched_at"),
|
||||
Index("ix_source_snapshots_checksum", "checksum_sha256"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
source_registry_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("source_registry.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
snapshot_key: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
source_version: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
snapshot_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
fetched_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False, server_default=func.now())
|
||||
source_url: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
checksum_sha256: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
crs: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
units: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
spatial_resolution_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
temporal_coverage_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
geographic_coverage_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
observed_schema_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
freshness_status: Mapped[str] = mapped_column(
|
||||
String(32), nullable=False, default="unknown", server_default="unknown"
|
||||
)
|
||||
ingest_status: Mapped[str] = mapped_column(
|
||||
String(32), nullable=False, default="registered", server_default="registered"
|
||||
)
|
||||
known_limitations_json: Mapped[list] = mapped_column(JSON, nullable=False, default=list)
|
||||
snapshot_metadata_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
source_registry: Mapped[SourceRegistry] = relationship("SourceRegistry", back_populates="snapshots")
|
||||
datasets: Mapped[list["Dataset"]] = relationship("Dataset", back_populates="source_snapshot")
|
||||
dataset_versions: Mapped[list["DatasetVersion"]] = relationship("DatasetVersion", back_populates="source_snapshot")
|
||||
quarantines: Mapped[list["DatasetQuarantine"]] = relationship("DatasetQuarantine", back_populates="source_snapshot")
|
||||
|
||||
|
||||
class Dataset(Base):
|
||||
__tablename__ = "datasets"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"valid_to IS NULL OR valid_from IS NULL OR valid_to >= valid_from",
|
||||
name="ck_datasets_temporal_valid_range",
|
||||
),
|
||||
CheckConstraint(
|
||||
"validation_status IN ('not_validated', 'passed', 'failed')",
|
||||
name="ck_datasets_validation_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"provenance_status IN ('complete', 'incomplete', 'not_applicable')",
|
||||
name="ck_datasets_provenance_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"lineage_status IN ('complete', 'incomplete', 'not_applicable')",
|
||||
name="ck_datasets_lineage_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"quarantine_status IN ('not_quarantined', 'quarantined')",
|
||||
name="ck_datasets_quarantine_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"ingest_key IS NULL OR btrim(ingest_key) <> ''",
|
||||
name="ck_datasets_ingest_key_not_blank",
|
||||
),
|
||||
UniqueConstraint("project_id", "ingest_key", name="uq_datasets_project_ingest_key"),
|
||||
Index(
|
||||
"ix_datasets_project_temporal_series_observed",
|
||||
"project_id",
|
||||
"temporal_series_key",
|
||||
"observed_at",
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
area_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("areas.id", ondelete="SET NULL"), nullable=True)
|
||||
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
dataset_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
source: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
storage_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
||||
original_filename: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
stored_filename: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
content_type: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
size_bytes: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
checksum_sha256: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
ingest_key: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
derived_from_dataset_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("datasets.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
crs: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
bounds_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
resolution_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
bands_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
dataset_role: Mapped[str] = mapped_column(String(32), nullable=False, default="source", server_default="source")
|
||||
source_name: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
reference_layer_name: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
source_metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
provenance_metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
source_registry_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("source_registry.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
source_snapshot_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("source_snapshots.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
data_contract_key: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
data_contract_version: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
validation_status: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
nullable=False,
|
||||
default="not_validated",
|
||||
server_default="not_validated",
|
||||
comment="not_validated | passed | failed",
|
||||
)
|
||||
validation_report_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
provenance_status: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
nullable=False,
|
||||
default="incomplete",
|
||||
server_default="incomplete",
|
||||
comment="complete | incomplete | not_applicable",
|
||||
)
|
||||
lineage_status: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
nullable=False,
|
||||
default="incomplete",
|
||||
server_default="incomplete",
|
||||
comment="complete | incomplete | not_applicable",
|
||||
)
|
||||
quarantine_status: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
nullable=False,
|
||||
default="not_quarantined",
|
||||
server_default="not_quarantined",
|
||||
comment="not_quarantined | quarantined",
|
||||
)
|
||||
imported_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
temporal_series_key: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
observed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
valid_from: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
valid_to: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
temporal_granularity: Mapped[str | None] = mapped_column(String(32), nullable=True)
|
||||
source_version: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
status: Mapped[str] = mapped_column(String(32), default="uploaded")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
project: Mapped[Project] = relationship("Project", back_populates="datasets")
|
||||
versions: Mapped[list["DatasetVersion"]] = relationship(
|
||||
"DatasetVersion",
|
||||
back_populates="dataset",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
vector_features: Mapped[list["VectorFeature"]] = relationship(
|
||||
"VectorFeature",
|
||||
back_populates="dataset",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
source_registry: Mapped[SourceRegistry | None] = relationship("SourceRegistry", back_populates="datasets")
|
||||
source_snapshot: Mapped[SourceSnapshot | None] = relationship("SourceSnapshot", back_populates="datasets")
|
||||
parent_lineage_edges: Mapped[list["DatasetLineageEdge"]] = relationship(
|
||||
"DatasetLineageEdge",
|
||||
foreign_keys="DatasetLineageEdge.parent_dataset_id",
|
||||
back_populates="parent_dataset",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
child_lineage_edges: Mapped[list["DatasetLineageEdge"]] = relationship(
|
||||
"DatasetLineageEdge",
|
||||
foreign_keys="DatasetLineageEdge.child_dataset_id",
|
||||
back_populates="child_dataset",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
quarantines: Mapped[list["DatasetQuarantine"]] = relationship(
|
||||
"DatasetQuarantine", back_populates="dataset", cascade="all, delete-orphan"
|
||||
)
|
||||
|
||||
|
||||
class DatasetVersion(Base):
|
||||
__tablename__ = "dataset_versions"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"valid_to IS NULL OR valid_from IS NULL OR valid_to >= valid_from",
|
||||
name="ck_dataset_versions_temporal_valid_range",
|
||||
),
|
||||
CheckConstraint(
|
||||
"validation_status IN ('not_validated', 'passed', 'failed')",
|
||||
name="ck_dataset_versions_validation_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"provenance_status IN ('complete', 'incomplete', 'not_applicable')",
|
||||
name="ck_dataset_versions_provenance_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"lineage_status IN ('complete', 'incomplete', 'not_applicable')",
|
||||
name="ck_dataset_versions_lineage_status",
|
||||
),
|
||||
CheckConstraint(
|
||||
"ingest_key IS NULL OR btrim(ingest_key) <> ''",
|
||||
name="ck_dataset_versions_ingest_key_not_blank",
|
||||
),
|
||||
Index("ix_dataset_versions_dataset_version", "dataset_id", "version", unique=True),
|
||||
UniqueConstraint("dataset_id", "ingest_key", name="uq_dataset_versions_dataset_ingest_key"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
dataset_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="CASCADE"), nullable=False)
|
||||
version: Mapped[int] = mapped_column(Integer, default=1)
|
||||
storage_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
||||
source_version: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
observed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
valid_from: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
valid_to: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
checksum_sha256: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
ingest_key: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
source_metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
provenance_metadata: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
source_registry_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("source_registry.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
source_snapshot_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("source_snapshots.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
data_contract_key: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
data_contract_version: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
validation_status: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
nullable=False,
|
||||
default="not_validated",
|
||||
server_default="not_validated",
|
||||
comment="not_validated | passed | failed",
|
||||
)
|
||||
validation_report_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
provenance_status: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
nullable=False,
|
||||
default="incomplete",
|
||||
server_default="incomplete",
|
||||
comment="complete | incomplete | not_applicable",
|
||||
)
|
||||
lineage_status: Mapped[str] = mapped_column(
|
||||
String(32),
|
||||
nullable=False,
|
||||
default="incomplete",
|
||||
server_default="incomplete",
|
||||
comment="complete | incomplete | not_applicable",
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
dataset: Mapped[Dataset] = relationship("Dataset", back_populates="versions")
|
||||
source_registry: Mapped[SourceRegistry | None] = relationship("SourceRegistry", back_populates="dataset_versions")
|
||||
source_snapshot: Mapped[SourceSnapshot | None] = relationship("SourceSnapshot", back_populates="dataset_versions")
|
||||
quarantines: Mapped[list["DatasetQuarantine"]] = relationship("DatasetQuarantine", back_populates="dataset_version")
|
||||
|
||||
|
||||
class DatasetLineageEdge(Base):
|
||||
"""Immutable relationship between input/output datasets and transforms."""
|
||||
|
||||
__tablename__ = "dataset_lineage_edges"
|
||||
__table_args__ = (
|
||||
CheckConstraint("parent_dataset_id <> child_dataset_id", name="ck_dataset_lineage_edges_distinct_datasets"),
|
||||
UniqueConstraint(
|
||||
"parent_dataset_id",
|
||||
"child_dataset_id",
|
||||
"relation_type",
|
||||
"transformation_name",
|
||||
name="uq_dataset_lineage_edges_relation",
|
||||
),
|
||||
Index("ix_dataset_lineage_edges_parent", "parent_dataset_id"),
|
||||
Index("ix_dataset_lineage_edges_child", "child_dataset_id"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
parent_dataset_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
child_dataset_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
parent_dataset_version_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("dataset_versions.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
child_dataset_version_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("dataset_versions.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
relation_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
transformation_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
transformation_version: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
parameters_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
input_checksum_sha256: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
output_checksum_sha256: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
parent_dataset: Mapped[Dataset] = relationship(
|
||||
"Dataset", foreign_keys=[parent_dataset_id], back_populates="parent_lineage_edges"
|
||||
)
|
||||
child_dataset: Mapped[Dataset] = relationship(
|
||||
"Dataset", foreign_keys=[child_dataset_id], back_populates="child_lineage_edges"
|
||||
)
|
||||
|
||||
|
||||
class DatasetQuarantine(Base):
|
||||
"""Durable fail-closed record for rejected or doubtful source artifacts."""
|
||||
|
||||
__tablename__ = "dataset_quarantines"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"dataset_id IS NOT NULL OR dataset_version_id IS NOT NULL OR source_snapshot_id IS NOT NULL",
|
||||
name="ck_dataset_quarantines_target_present",
|
||||
),
|
||||
CheckConstraint(
|
||||
"status IN ('quarantined', 'released', 'rejected')",
|
||||
name="ck_dataset_quarantines_status",
|
||||
),
|
||||
Index("ix_dataset_quarantines_dataset_status", "dataset_id", "status"),
|
||||
Index("ix_dataset_quarantines_snapshot_status", "source_snapshot_id", "status"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
dataset_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
dataset_version_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("dataset_versions.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
source_snapshot_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True), ForeignKey("source_snapshots.id", ondelete="SET NULL"), nullable=True
|
||||
)
|
||||
stage: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
reason_code: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
details_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
artifact_path: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
artifact_checksum_sha256: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
status: Mapped[str] = mapped_column(
|
||||
String(32), nullable=False, default="quarantined", server_default="quarantined"
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
resolved_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
resolved_by: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
|
||||
dataset: Mapped[Dataset | None] = relationship("Dataset", back_populates="quarantines")
|
||||
dataset_version: Mapped[DatasetVersion | None] = relationship("DatasetVersion", back_populates="quarantines")
|
||||
source_snapshot: Mapped[SourceSnapshot | None] = relationship("SourceSnapshot", back_populates="quarantines")
|
||||
|
||||
|
||||
class VectorFeature(Base):
|
||||
__tablename__ = "vector_features"
|
||||
__table_args__ = (
|
||||
Index("ix_vector_features_dataset_id", "dataset_id"),
|
||||
Index("ix_vector_features_geometry", "geometry", postgresql_using="gist"),
|
||||
Index("ix_vector_features_dataset_source_feature", "dataset_id", "source_feature_id"),
|
||||
Index(
|
||||
"ix_vector_features_dataset_municipality",
|
||||
"dataset_id",
|
||||
text("(properties_json ->> 'municipality')"),
|
||||
),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
dataset_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="CASCADE"), nullable=False)
|
||||
feature_class: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
source_feature_id: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
properties_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
geometry: Mapped[str] = mapped_column(Geometry("Geometry", srid=4326, spatial_index=False), nullable=False)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
dataset: Mapped[Dataset] = relationship("Dataset", back_populates="vector_features")
|
||||
|
||||
|
||||
class AnalysisRun(Base):
|
||||
__tablename__ = "analysis_runs"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
area_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("areas.id", ondelete="SET NULL"), nullable=True)
|
||||
dataset_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="SET NULL"), nullable=True)
|
||||
job_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="SET NULL"), nullable=True)
|
||||
analysis_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||
model_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||
model_version: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
parameters_json: Mapped[dict] = mapped_column(JSON, nullable=False)
|
||||
result_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
|
||||
|
||||
class Detection(Base):
|
||||
__tablename__ = "detections"
|
||||
__table_args__ = (
|
||||
Index("ix_detections_project_id", "project_id"),
|
||||
Index("ix_detections_dataset_id", "dataset_id"),
|
||||
Index("ix_detections_analysis_run_id", "analysis_run_id"),
|
||||
Index("ix_detections_class_name", "class_name"),
|
||||
Index("ix_detections_geometry", "geometry", postgresql_using="gist"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
dataset_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="SET NULL"), nullable=True)
|
||||
analysis_run_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("analysis_runs.id", ondelete="SET NULL"), nullable=True)
|
||||
job_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="SET NULL"), nullable=True)
|
||||
model_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
model_version: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
class_name: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
confidence: Mapped[float] = mapped_column(Float, nullable=False)
|
||||
geometry: Mapped[str] = mapped_column(Geometry("Geometry", srid=4326, spatial_index=False), nullable=False)
|
||||
bbox_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
source_tile_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
||||
properties_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class Segmentation(Base):
|
||||
__tablename__ = "segmentations"
|
||||
__table_args__ = (
|
||||
Index("ix_segmentations_project_id", "project_id"),
|
||||
Index("ix_segmentations_dataset_id", "dataset_id"),
|
||||
Index("ix_segmentations_analysis_run_id", "analysis_run_id"),
|
||||
Index("ix_segmentations_job_id", "job_id"),
|
||||
Index("ix_segmentations_class_name", "class_name"),
|
||||
Index("ix_segmentations_geometry", "geometry", postgresql_using="gist"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
dataset_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="SET NULL"), nullable=True)
|
||||
job_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="SET NULL"), nullable=True)
|
||||
analysis_run_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("analysis_runs.id", ondelete="SET NULL"), nullable=True)
|
||||
model_name: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
model_version: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
class_name: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
confidence: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
geometry: Mapped[str] = mapped_column(Geometry("MultiPolygon", srid=4326, spatial_index=False), nullable=False)
|
||||
bbox_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
area_m2: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
mask_path: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
source_tile_path: Mapped[str | None] = mapped_column(String(500), nullable=True)
|
||||
tile_index: Mapped[int | None] = mapped_column(Integer, nullable=True)
|
||||
properties_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
provenance_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class QualityCheck(Base):
|
||||
__tablename__ = "quality_checks"
|
||||
__table_args__ = (
|
||||
Index("ix_quality_checks_project_id", "project_id"),
|
||||
Index("ix_quality_checks_reference_dataset_id", "reference_dataset_id"),
|
||||
Index("ix_quality_checks_candidate_dataset_id", "candidate_dataset_id"),
|
||||
Index("ix_quality_checks_analysis_run_id", "analysis_run_id"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
job_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="SET NULL"), nullable=True)
|
||||
analysis_run_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("analysis_runs.id", ondelete="SET NULL"), nullable=True)
|
||||
candidate_dataset_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="SET NULL"), nullable=True)
|
||||
reference_dataset_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="CASCADE"), nullable=False)
|
||||
check_type: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||
score: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
parameters_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
findings_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
completed_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
|
||||
class Metric(Base):
|
||||
__tablename__ = "metrics"
|
||||
__table_args__ = (
|
||||
Index("ix_metrics_quality_check_id", "quality_check_id"),
|
||||
Index("ix_metrics_analysis_run_id", "analysis_run_id"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
quality_check_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("quality_checks.id", ondelete="CASCADE"), nullable=True)
|
||||
analysis_run_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("analysis_runs.id", ondelete="SET NULL"), nullable=True)
|
||||
metric_key: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
metric_value: Mapped[float | None] = mapped_column(Float, nullable=True)
|
||||
metric_unit: Mapped[str | None] = mapped_column(String(64), nullable=True)
|
||||
label: Mapped[str | None] = mapped_column(String(120), nullable=True)
|
||||
metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class DetectionReview(Base):
|
||||
__tablename__ = "detection_reviews"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"evidence_role IN ('false_positive', 'false_negative')",
|
||||
name="ck_detection_reviews_evidence_role",
|
||||
),
|
||||
CheckConstraint(
|
||||
"decision IN ('confirmed_model_false_positive', 'confirmed_model_false_negative', "
|
||||
"'reference_gap_or_change', 'qa_alignment_mismatch', "
|
||||
"'imagery_obscured_or_uncertain', 'uncertain', 'unreviewed')",
|
||||
name="ck_detection_reviews_decision",
|
||||
),
|
||||
UniqueConstraint(
|
||||
"quality_check_id",
|
||||
"evidence_role",
|
||||
"evidence_feature_id",
|
||||
name="uq_detection_reviews_evidence",
|
||||
),
|
||||
Index("ix_detection_reviews_project_id", "project_id"),
|
||||
Index("ix_detection_reviews_quality_check_id", "quality_check_id"),
|
||||
Index("ix_detection_reviews_analysis_run_id", "analysis_run_id"),
|
||||
Index("ix_detection_reviews_decision", "decision"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
quality_check_id: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("quality_checks.id", ondelete="CASCADE"),
|
||||
nullable=False,
|
||||
)
|
||||
analysis_run_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("analysis_runs.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
evidence_role: Mapped[str] = mapped_column(String(32), nullable=False)
|
||||
evidence_feature_id: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
detection_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("detections.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
reference_feature_id: Mapped[uuid.UUID | None] = mapped_column(
|
||||
UUID(as_uuid=True),
|
||||
ForeignKey("vector_features.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
)
|
||||
decision: Mapped[str] = mapped_column(String(64), nullable=False, default="unreviewed", server_default="unreviewed")
|
||||
notes: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
reviewed_by: Mapped[str] = mapped_column(String(120), nullable=False, default="operator", server_default="operator")
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
|
||||
class Export(Base):
|
||||
__tablename__ = "exports"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
analysis_run_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("analysis_runs.id", ondelete="SET NULL"), nullable=True)
|
||||
export_type: Mapped[str] = mapped_column(String(64), nullable=False)
|
||||
storage_path: Mapped[str] = mapped_column(String(500), nullable=False)
|
||||
metadata_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class Job(Base):
|
||||
__tablename__ = "jobs"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
job_type: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(32), nullable=False, default="queued")
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
dataset_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="SET NULL"), nullable=True)
|
||||
input_dataset_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="SET NULL"), nullable=True)
|
||||
output_dataset_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("datasets.id", ondelete="SET NULL"), nullable=True)
|
||||
parameters_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
result_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
|
||||
class AoiOperation(Base):
|
||||
__tablename__ = "aoi_operations"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"status IN ('queued', 'running', 'partial', 'success', 'failed', 'cancelled')",
|
||||
name="ck_aoi_operations_status",
|
||||
),
|
||||
Index("ix_aoi_operations_project_status", "project_id", "status"),
|
||||
Index("ix_aoi_operations_geometry", "geometry", postgresql_using="gist"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
project_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("projects.id", ondelete="CASCADE"), nullable=False)
|
||||
area_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("areas.id", ondelete="SET NULL"), nullable=True)
|
||||
parent_job_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="SET NULL"), nullable=True)
|
||||
operation_type: Mapped[str] = mapped_column(String(128), nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(32), nullable=False, default="queued")
|
||||
geometry: Mapped[str] = mapped_column(Geometry("MultiPolygon", srid=4326, spatial_index=False), nullable=False)
|
||||
request_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
plan_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
|
||||
result_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
|
||||
|
||||
class AoiOperationPartition(Base):
|
||||
__tablename__ = "aoi_operation_partitions"
|
||||
__table_args__ = (
|
||||
CheckConstraint(
|
||||
"status IN ('queued', 'running', 'success', 'failed', 'skipped')",
|
||||
name="ck_aoi_operation_partitions_status",
|
||||
),
|
||||
UniqueConstraint("operation_id", "partition_key", name="uq_aoi_operation_partition_key"),
|
||||
Index("ix_aoi_operation_partitions_operation_status", "operation_id", "status"),
|
||||
Index("ix_aoi_operation_partitions_geometry", "geometry", postgresql_using="gist"),
|
||||
)
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
operation_id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), ForeignKey("aoi_operations.id", ondelete="CASCADE"), nullable=False)
|
||||
child_job_id: Mapped[uuid.UUID | None] = mapped_column(UUID(as_uuid=True), ForeignKey("jobs.id", ondelete="SET NULL"), nullable=True)
|
||||
partition_key: Mapped[str] = mapped_column(String(255), nullable=False)
|
||||
provider_key: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
product_key: Mapped[str] = mapped_column(String(120), nullable=False)
|
||||
ordinal: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||
status: Mapped[str] = mapped_column(String(32), nullable=False, default="queued")
|
||||
geometry: Mapped[str] = mapped_column(Geometry("MultiPolygon", srid=4326, spatial_index=False), nullable=False)
|
||||
attempt_count: Mapped[int] = mapped_column(Integer, nullable=False, default=0)
|
||||
max_attempts: Mapped[int] = mapped_column(Integer, nullable=False, default=3)
|
||||
checkpoint_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
result_json: Mapped[dict | None] = mapped_column(JSON, nullable=True)
|
||||
error_message: Mapped[str | None] = mapped_column(Text, nullable=True)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
started_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
finished_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
|
||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
|
||||
Reference in New Issue
Block a user