File size: 4,344 Bytes
41d16b3
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
66006d5
 
41d16b3
 
 
 
 
 
 
 
 
669f7f8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
import uuid
from datetime import datetime, timezone

from sqlalchemy import Boolean, Column, Date, DateTime, Float, ForeignKey, Integer, String, Text
from sqlalchemy.orm import relationship

from ..database import Base


def _utcnow():
    return datetime.now(timezone.utc)


class DdaZone(Base):
    __tablename__ = "dda_zones"

    id = Column(Integer, primary_key=True, index=True)
    name = Column(String(128), unique=True, nullable=False, index=True)
    mode = Column(String(32), default="admin")  # admin | grid_parent
    created_at = Column(DateTime, default=_utcnow)

    villages = relationship("DdaVillage", back_populates="zone", cascade="all, delete-orphan")


class DdaVillage(Base):
    __tablename__ = "dda_villages"

    id = Column(Integer, primary_key=True, index=True)
    zone_id = Column(Integer, ForeignKey("dda_zones.id"), nullable=False, index=True)
    name = Column(String(128), nullable=False, index=True)
    created_at = Column(DateTime, default=_utcnow)

    zone = relationship("DdaZone", back_populates="villages")
    images = relationship("ImageAsset", back_populates="village")


class ImageAsset(Base):
    """Centralized satellite / drone image (FR-01, FR-02)."""
    __tablename__ = "dda_image_assets"

    id = Column(Integer, primary_key=True, index=True)
    uuid = Column(String(36), unique=True, nullable=False, default=lambda: str(uuid.uuid4()), index=True)
    zone_id = Column(Integer, ForeignKey("dda_zones.id"), nullable=True, index=True)
    village_id = Column(Integer, ForeignKey("dda_villages.id"), nullable=True, index=True)
    area_name = Column(String(128), default="")
    year = Column(Integer, nullable=True, index=True)
    grid_id = Column(String(64), default="", index=True)

    capture_date = Column(Date, nullable=True)
    source = Column(String(32), default="satellite")  # satellite | drone
    format = Column(String(32), default="geotiff")
    original_filename = Column(String(255), default="")

    file_path = Column(String(512), nullable=False)
    thumb_path = Column(String(512), default="")
    preview_path = Column(String(512), default="")

    crs = Column(String(64), default="")
    bounds_json = Column(Text, default="")  # WGS84 [west, south, east, north]
    has_georef = Column(Boolean, default=False)
    manual_location_json = Column(Text, default="")

    width = Column(Integer, default=0)
    height = Column(Integer, default=0)
    file_size_bytes = Column(Integer, default=0)

    uploaded_by = Column(Integer, ForeignKey("users.id"), nullable=True)
    created_at = Column(DateTime, default=_utcnow)

    zone = relationship("DdaZone")
    village = relationship("DdaVillage", back_populates="images")


class DetectionJob(Base):
    """Async change detection job (FR-04 skeleton)."""
    __tablename__ = "dda_detection_jobs"

    id = Column(Integer, primary_key=True, index=True)
    status = Column(String(32), default="queued", index=True)  # queued|running|completed|failed
    base_image_id = Column(Integer, ForeignKey("dda_image_assets.id"), nullable=True)
    comparison_image_id = Column(Integer, ForeignKey("dda_image_assets.id"), nullable=True)
    method = Column(String(64), default="AI-Based Deep Learning")
    params_json = Column(Text, default="{}")
    run_id = Column(Integer, ForeignKey("detection_runs.id"), nullable=True)
    error_message = Column(Text, default="")
    notify_email = Column(String(255), default="")
    created_by = Column(Integer, ForeignKey("users.id"), nullable=True)
    started_at = Column(DateTime, nullable=True)
    completed_at = Column(DateTime, nullable=True)
    created_at = Column(DateTime, default=_utcnow)


class RegionReview(Base):
    """Per-region human review state (FR-08)."""
    __tablename__ = "dda_region_reviews"

    id = Column(Integer, primary_key=True, index=True)
    run_id = Column(Integer, ForeignKey("detection_runs.id"), nullable=False, index=True)
    region_id = Column(Integer, nullable=False, index=True)
    status = Column(String(32), default="pending")  # pending|confirmed|false_positive|submitted
    reviewer_id = Column(Integer, ForeignKey("users.id"), nullable=True)
    notes = Column(Text, default="")
    reviewed_at = Column(DateTime, nullable=True)
    submitted_at = Column(DateTime, nullable=True)
    created_at = Column(DateTime, default=_utcnow)