FastAPI + Celery + Next.js + Postgres/Redis app with company monitoring, source collection, LLM-based change analysis, enrichment, and account security (Turnstile, escalating lockout, email verification).
45 lines
1.8 KiB
Python
45 lines
1.8 KiB
Python
from __future__ import annotations
|
|
|
|
import uuid
|
|
from typing import Any
|
|
|
|
from sqlalchemy import JSON, Enum, Float, ForeignKey, String
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
|
|
from app.db.base import Base, TimestampMixin, UUIDPrimaryKeyMixin
|
|
from app.models.enums import ChangeStatus, ChangeType, SeverityLevel
|
|
|
|
|
|
class DetectedChange(Base, UUIDPrimaryKeyMixin, TimestampMixin):
|
|
__tablename__ = "detected_changes"
|
|
|
|
company_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey("companies.id", ondelete="CASCADE"), index=True
|
|
)
|
|
source_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey("sources.id", ondelete="CASCADE"), index=True
|
|
)
|
|
monitoring_run_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey("monitoring_runs.id", ondelete="CASCADE"), index=True
|
|
)
|
|
previous_snapshot_id: Mapped[uuid.UUID | None] = mapped_column(
|
|
ForeignKey("snapshots.id", ondelete="SET NULL"), nullable=True
|
|
)
|
|
current_snapshot_id: Mapped[uuid.UUID] = mapped_column(
|
|
ForeignKey("snapshots.id", ondelete="CASCADE")
|
|
)
|
|
change_type: Mapped[ChangeType] = mapped_column(Enum(ChangeType, native_enum=False, length=30))
|
|
raw_diff: Mapped[dict[str, Any]] = mapped_column(JSON, default=dict)
|
|
significance_score: Mapped[float] = mapped_column(Float)
|
|
confidence_score: Mapped[float] = mapped_column(Float)
|
|
severity: Mapped[SeverityLevel] = mapped_column(
|
|
Enum(SeverityLevel, native_enum=False, length=20)
|
|
)
|
|
status: Mapped[ChangeStatus] = mapped_column(
|
|
Enum(ChangeStatus, native_enum=False, length=20), default=ChangeStatus.NEW
|
|
)
|
|
# Short human-readable label, e.g. "3 new job postings detected" -
|
|
# populated deterministically here; Phase 7's LLM may later add a
|
|
# richer "why it matters" narrative on top without replacing this.
|
|
summary: Mapped[str] = mapped_column(String(500))
|