NewsAtlas/backend/app/models.py

92 lines
4.2 KiB
Python

import datetime as dt
from sqlalchemy import DateTime, Float, Integer, String, Text, UniqueConstraint
from sqlalchemy.orm import Mapped, mapped_column
from .db import Base
class Article(Base):
__tablename__ = "articles"
__table_args__ = (UniqueConstraint("url", name="uq_article_url"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
source: Mapped[str] = mapped_column(String(128), index=True)
source_bias: Mapped[str] = mapped_column(String(64), default="")
title: Mapped[str] = mapped_column(Text)
url: Mapped[str] = mapped_column(Text, index=True)
summary: Mapped[str] = mapped_column(Text, default="")
published_at: Mapped[dt.datetime] = mapped_column(DateTime, index=True)
fetched_at: Mapped[dt.datetime] = mapped_column(DateTime, default=dt.datetime.utcnow)
location_name: Mapped[str | None] = mapped_column(String(128), nullable=True, index=True)
country: Mapped[str | None] = mapped_column(String(128), nullable=True)
lat: Mapped[float | None] = mapped_column(Float, nullable=True)
lon: Mapped[float | None] = mapped_column(Float, nullable=True)
# Rounded lat/lon grid key articles are clustered on, e.g. "31.5:34.5"
cluster_key: Mapped[str | None] = mapped_column(String(64), nullable=True, index=True)
class MarketPrice(Base):
__tablename__ = "market_prices"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
symbol: Mapped[str] = mapped_column(String(32), index=True)
label: Mapped[str] = mapped_column(String(128))
category: Mapped[str] = mapped_column(String(32)) # "index" | "commodity"
price: Mapped[float] = mapped_column(Float)
currency: Mapped[str] = mapped_column(String(8), default="USD")
change_pct: Mapped[float | None] = mapped_column(Float, nullable=True)
recorded_at: Mapped[dt.datetime] = mapped_column(DateTime, index=True, default=dt.datetime.utcnow)
class MarketSpike(Base):
__tablename__ = "market_spikes"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
symbol: Mapped[str] = mapped_column(String(32), index=True)
label: Mapped[str] = mapped_column(String(128))
from_price: Mapped[float] = mapped_column(Float)
to_price: Mapped[float] = mapped_column(Float)
pct_change: Mapped[float] = mapped_column(Float)
# This instrument's recent typical (mean absolute) poll-to-poll move,
# for comparison — null if there wasn't enough price history yet to
# compute one (see market_spike_min_samples). Lets the UI show "Nx this
# instrument's normal move" instead of just the raw percentage.
baseline_volatility_pct: Mapped[float | None] = mapped_column(Float, nullable=True)
window_start: Mapped[dt.datetime] = mapped_column(DateTime)
window_end: Mapped[dt.datetime] = mapped_column(DateTime)
detected_at: Mapped[dt.datetime] = mapped_column(DateTime, index=True, default=dt.datetime.utcnow)
# JSON-encoded list of Article.id, ranked most-to-least likely relevant.
article_ids_json: Mapped[str] = mapped_column(Text, default="[]")
# JSON-encoded [[word, count], ...] — plain word-frequency across every
# article in the window's titles, stopwords dropped. A heuristic hint at
# "likely factors," not an explanation; see textutil.py.
top_keywords_json: Mapped[str] = mapped_column(Text, default="[]")
class ConflictEvent(Base):
__tablename__ = "conflict_events"
__table_args__ = (UniqueConstraint("source", "external_id", name="uq_conflict_event"),)
id: Mapped[int] = mapped_column(Integer, primary_key=True)
source: Mapped[str] = mapped_column(String(32), default="acled")
external_id: Mapped[str] = mapped_column(String(64))
event_type: Mapped[str] = mapped_column(String(128))
actor1: Mapped[str] = mapped_column(String(256), default="")
actor2: Mapped[str] = mapped_column(String(256), default="")
fatalities: Mapped[int | None] = mapped_column(Integer, nullable=True)
notes: Mapped[str] = mapped_column(Text, default="")
location_name: Mapped[str] = mapped_column(String(128), default="")
country: Mapped[str] = mapped_column(String(128), default="")
lat: Mapped[float] = mapped_column(Float)
lon: Mapped[float] = mapped_column(Float)
event_date: Mapped[dt.datetime] = mapped_column(DateTime, index=True)