Extract reusable literal keywords once per source revision, assign complete news packages to long-lived events, and research all event members with immutable evidence and references. Add additive research storage, vocabulary snapshots, the two-stage workspace, isolated acceptance coverage, and a paused rollout with an initial fixed 50-news production trial.
143 lines
5.2 KiB
Python
143 lines
5.2 KiB
Python
"""Additive storage: original news and legacy AI tables are never written here."""
|
|
|
|
from datetime import datetime
|
|
from sqlalchemy import (
|
|
BigInteger,
|
|
DateTime,
|
|
ForeignKey,
|
|
Integer,
|
|
String,
|
|
Text,
|
|
UniqueConstraint,
|
|
func,
|
|
)
|
|
from sqlalchemy.dialects.postgresql import JSONB
|
|
from sqlalchemy.orm import Mapped, mapped_column
|
|
from alphapulse.core.db import Base
|
|
|
|
|
|
class VocabularyVersion(Base):
|
|
__tablename__ = "news_research_vocabulary_versions"
|
|
revision: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=False)
|
|
entries: Mapped[list] = mapped_column(JSONB)
|
|
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
|
|
|
|
|
class ResearchSettings(Base):
|
|
__tablename__ = "news_research_settings"
|
|
id: Mapped[int] = mapped_column(primary_key=True)
|
|
config: Mapped[dict] = mapped_column(JSONB)
|
|
enabled: Mapped[bool] = mapped_column(default=False)
|
|
control_revision: Mapped[int] = mapped_column(
|
|
Integer, default=0, server_default="0"
|
|
)
|
|
start_at: Mapped[datetime] = mapped_column(DateTime(timezone=True))
|
|
|
|
|
|
class Package(Base):
|
|
__tablename__ = "news_research_packages"
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
news_id: Mapped[int] = mapped_column(ForeignKey("news_items.id"), unique=True)
|
|
content_hash: Mapped[str] = mapped_column(String(64))
|
|
state: Mapped[str] = mapped_column(String(20), default="index_pending", index=True)
|
|
keywords: Mapped[list] = mapped_column(JSONB, default=list)
|
|
error: Mapped[str | None] = mapped_column(Text)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now()
|
|
)
|
|
|
|
|
|
class Attempt(Base):
|
|
__tablename__ = "news_research_attempts"
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
package_id: Mapped[int] = mapped_column(
|
|
ForeignKey("news_research_packages.id"), index=True
|
|
)
|
|
content_hash: Mapped[str] = mapped_column(String(64))
|
|
stage: Mapped[str] = mapped_column(String(16))
|
|
state: Mapped[str] = mapped_column(String(16))
|
|
input: Mapped[dict] = mapped_column(JSONB)
|
|
output: Mapped[dict | None] = mapped_column(JSONB)
|
|
rules: Mapped[dict] = mapped_column(JSONB)
|
|
usage: Mapped[dict | None] = mapped_column(JSONB)
|
|
error: Mapped[str | None] = mapped_column(Text)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now()
|
|
)
|
|
|
|
|
|
class Event(Base):
|
|
__tablename__ = "news_research_events"
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
title: Mapped[str] = mapped_column(Text)
|
|
tags: Mapped[list] = mapped_column(JSONB, default=list)
|
|
revision: Mapped[int] = mapped_column(Integer, default=1)
|
|
state: Mapped[str] = mapped_column(String(16), default="pending", index=True)
|
|
result: Mapped[dict | None] = mapped_column(JSONB)
|
|
error: Mapped[str | None] = mapped_column(Text)
|
|
dirty_since: Mapped[datetime | None] = mapped_column(DateTime(timezone=True))
|
|
due_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now(), index=True
|
|
)
|
|
updated_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now()
|
|
)
|
|
|
|
|
|
class Membership(Base):
|
|
__tablename__ = "news_research_memberships"
|
|
event_id: Mapped[int] = mapped_column(
|
|
ForeignKey("news_research_events.id"), primary_key=True
|
|
)
|
|
package_id: Mapped[int] = mapped_column(
|
|
ForeignKey("news_research_packages.id"), primary_key=True
|
|
)
|
|
evidence: Mapped[str] = mapped_column(Text)
|
|
reason: Mapped[str] = mapped_column(Text)
|
|
change_kind: Mapped[str] = mapped_column(String(20))
|
|
|
|
|
|
class Version(Base):
|
|
__tablename__ = "news_research_versions"
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
event_id: Mapped[int] = mapped_column(
|
|
ForeignKey("news_research_events.id"), index=True
|
|
)
|
|
revision: Mapped[int] = mapped_column(Integer)
|
|
state: Mapped[str] = mapped_column(String(16))
|
|
input: Mapped[dict] = mapped_column(JSONB)
|
|
output: Mapped[dict | None] = mapped_column(JSONB)
|
|
rules: Mapped[dict] = mapped_column(JSONB)
|
|
usage: Mapped[dict | None] = mapped_column(JSONB)
|
|
evidence: Mapped[list] = mapped_column(JSONB, default=list)
|
|
error: Mapped[str | None] = mapped_column(Text)
|
|
created_at: Mapped[datetime] = mapped_column(
|
|
DateTime(timezone=True), server_default=func.now()
|
|
)
|
|
|
|
|
|
class Reference(Base):
|
|
__tablename__ = "news_research_references"
|
|
__table_args__ = (UniqueConstraint("version_id", "target_version_id"),)
|
|
id: Mapped[int] = mapped_column(BigInteger, primary_key=True)
|
|
version_id: Mapped[int] = mapped_column(
|
|
ForeignKey("news_research_versions.id"), index=True
|
|
)
|
|
target_version_id: Mapped[int] = mapped_column(
|
|
ForeignKey("news_research_versions.id")
|
|
)
|
|
role: Mapped[str] = mapped_column(String(20))
|
|
reason: Mapped[str] = mapped_column(Text)
|
|
|
|
|
|
TABLES = [
|
|
VocabularyVersion.__table__,
|
|
ResearchSettings.__table__,
|
|
Package.__table__,
|
|
Attempt.__table__,
|
|
Event.__table__,
|
|
Membership.__table__,
|
|
Version.__table__,
|
|
Reference.__table__,
|
|
]
|