feat: add news_items table and summary model column

This commit is contained in:
Sirius DevOps 2026-08-27 22:40:50 -04:00
parent 4b00eed566
commit cbc573b830
2 changed files with 86 additions and 2 deletions

View file

@ -0,0 +1,64 @@
"""news_items table + article_summaries.model
Revision ID: 005_news_items
Revises: 004_camera_enum
Create Date: 2026-08-28
"""
from alembic import op
# revision identifiers, used by Alembic.
revision = "005_news_items"
down_revision = "004_camera_enum"
branch_labels = None
depends_on = None
def upgrade() -> None:
# Idempotent DDL: ingest (summarizer ensure_tables) may create the same
# shapes first depending on container startup order. IF NOT EXISTS makes
# both orders safe — whichever runs first wins, the other no-ops.
# One statement per op.execute: asyncpg rejects multi-command prepared
# statements (same style as 003_news).
op.execute(
"""
ALTER TABLE article_summaries
ADD COLUMN IF NOT EXISTS model TEXT
"""
)
op.execute(
"""
CREATE TABLE IF NOT EXISTS news_items (
id SERIAL PRIMARY KEY,
summary_id INTEGER REFERENCES article_summaries(id) ON DELETE CASCADE,
kind TEXT NOT NULL,
headline TEXT NOT NULL,
importance TEXT NOT NULL,
location_name TEXT,
lat DOUBLE PRECISION,
lon DOUBLE PRECISION,
location_confidence TEXT,
category TEXT,
url TEXT,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
)
"""
)
op.execute(
"""
CREATE INDEX IF NOT EXISTS ix_news_items_kind_created
ON news_items (kind, created_at DESC)
"""
)
op.execute(
"""
CREATE INDEX IF NOT EXISTS ix_news_items_map_bbox
ON news_items (lon, lat)
WHERE kind = 'map' AND lat IS NOT NULL AND lon IS NOT NULL
"""
)
def downgrade() -> None:
op.execute("DROP TABLE IF EXISTS news_items")
op.execute("ALTER TABLE article_summaries DROP COLUMN IF EXISTS model")

View file

@ -185,8 +185,8 @@ Index("ix_fires_bbox", fires.c.longitude, fires.c.latitude)
# ── News pipeline (scraper + summarizer) ────────────────────────────────── # ── News pipeline (scraper + summarizer) ──────────────────────────────────
# Written by the vendored news-scraper (Scrapy) / news-summarizer (Gemini) # Written by the vendored news-scraper (Scrapy) / news-summarizer services;
# services; schema must match the idempotent alembic migration 003_news. # schema must match the idempotent alembic migrations 003_news + 005_news_items.
articles = Table( articles = Table(
"articles", "articles",
@ -209,6 +209,26 @@ article_summaries = Table(
Column("summary_text", Text, nullable=False), Column("summary_text", Text, nullable=False),
Column("batch_timestamp", DateTime(timezone=True), Column("batch_timestamp", DateTime(timezone=True),
server_default=func.now(), nullable=False), server_default=func.now(), nullable=False),
Column("model", Text), # LLM id used for this batch; nullable for old rows
) )
Index("ix_article_summaries_batch_timestamp", article_summaries.c.batch_timestamp) Index("ix_article_summaries_batch_timestamp", article_summaries.c.batch_timestamp)
news_items = Table(
"news_items",
metadata,
Column("id", Integer, primary_key=True, autoincrement=True),
Column("summary_id", Integer),
Column("kind", Text, nullable=False),
Column("headline", Text, nullable=False),
Column("importance", Text, nullable=False),
Column("location_name", Text),
Column("lat", Float),
Column("lon", Float),
Column("location_confidence", Text),
Column("category", Text),
Column("url", Text),
Column("created_at", DateTime(timezone=True), server_default=func.now(), nullable=False),
)
Index("ix_news_items_kind_created", news_items.c.kind, news_items.c.created_at)