mirror of
https://github.com/MODSetter/SurfSense.git
synced 2026-04-25 00:36:31 +02:00
feat: add reports table and report status enum for generated Markdown reports
This commit is contained in:
parent
1d1e2c6b48
commit
b6c0406d10
2 changed files with 135 additions and 0 deletions
88
surfsense_backend/alembic/versions/99_add_reports_table.py
Normal file
88
surfsense_backend/alembic/versions/99_add_reports_table.py
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
"""Add reports table
|
||||
|
||||
Revision ID: 99
|
||||
Revises: 98
|
||||
Create Date: 2026-02-11
|
||||
|
||||
Adds report_status enum and reports table for storing generated Markdown reports.
|
||||
"""
|
||||
|
||||
from collections.abc import Sequence
|
||||
|
||||
from alembic import op
|
||||
|
||||
revision: str = "99"
|
||||
down_revision: str | None = "98"
|
||||
branch_labels: str | Sequence[str] | None = None
|
||||
depends_on: str | Sequence[str] | None = None
|
||||
|
||||
|
||||
def upgrade() -> None:
|
||||
# Create the report_status enum type
|
||||
op.execute(
|
||||
"""
|
||||
DO $$ BEGIN
|
||||
CREATE TYPE report_status AS ENUM ('ready', 'failed');
|
||||
EXCEPTION
|
||||
WHEN duplicate_object THEN null;
|
||||
END $$;
|
||||
"""
|
||||
)
|
||||
|
||||
# Create the reports table
|
||||
op.execute(
|
||||
"""
|
||||
CREATE TABLE IF NOT EXISTS reports (
|
||||
id SERIAL PRIMARY KEY,
|
||||
title VARCHAR(500) NOT NULL,
|
||||
content TEXT,
|
||||
report_metadata JSONB,
|
||||
status report_status NOT NULL DEFAULT 'ready',
|
||||
report_style VARCHAR(100),
|
||||
search_space_id INTEGER NOT NULL
|
||||
REFERENCES searchspaces(id) ON DELETE CASCADE,
|
||||
thread_id INTEGER
|
||||
REFERENCES new_chat_threads(id) ON DELETE SET NULL,
|
||||
created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()
|
||||
);
|
||||
"""
|
||||
)
|
||||
|
||||
# Add indexes
|
||||
op.execute(
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS ix_reports_status
|
||||
ON reports(status);
|
||||
"""
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS ix_reports_search_space_id
|
||||
ON reports(search_space_id);
|
||||
"""
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS ix_reports_thread_id
|
||||
ON reports(thread_id);
|
||||
"""
|
||||
)
|
||||
|
||||
op.execute(
|
||||
"""
|
||||
CREATE INDEX IF NOT EXISTS ix_reports_created_at
|
||||
ON reports(created_at);
|
||||
"""
|
||||
)
|
||||
|
||||
|
||||
def downgrade() -> None:
|
||||
op.execute("DROP INDEX IF EXISTS ix_reports_created_at")
|
||||
op.execute("DROP INDEX IF EXISTS ix_reports_thread_id")
|
||||
op.execute("DROP INDEX IF EXISTS ix_reports_search_space_id")
|
||||
op.execute("DROP INDEX IF EXISTS ix_reports_status")
|
||||
op.execute("DROP TABLE IF EXISTS reports")
|
||||
op.execute("DROP TYPE IF EXISTS report_status")
|
||||
|
||||
|
|
@ -100,6 +100,11 @@ class PodcastStatus(str, Enum):
|
|||
FAILED = "failed"
|
||||
|
||||
|
||||
class ReportStatus(str, Enum):
|
||||
READY = "ready"
|
||||
FAILED = "failed"
|
||||
|
||||
|
||||
class DocumentStatus:
|
||||
"""
|
||||
Helper class for document processing status (stored as JSONB).
|
||||
|
|
@ -1031,6 +1036,42 @@ class Podcast(BaseModel, TimestampMixin):
|
|||
thread = relationship("NewChatThread")
|
||||
|
||||
|
||||
class Report(BaseModel, TimestampMixin):
|
||||
"""Report model for storing generated Markdown reports."""
|
||||
|
||||
__tablename__ = "reports"
|
||||
|
||||
title = Column(String(500), nullable=False)
|
||||
content = Column(Text, nullable=True) # Markdown body
|
||||
report_metadata = Column(JSONB, nullable=True) # section headings, word count, etc.
|
||||
status = Column(
|
||||
SQLAlchemyEnum(
|
||||
ReportStatus,
|
||||
name="report_status",
|
||||
create_type=False,
|
||||
values_callable=lambda x: [e.value for e in x],
|
||||
),
|
||||
nullable=False,
|
||||
default=ReportStatus.READY,
|
||||
server_default="ready",
|
||||
index=True,
|
||||
)
|
||||
report_style = Column(String(100), nullable=True) # e.g. "executive_summary", "deep_research"
|
||||
|
||||
search_space_id = Column(
|
||||
Integer, ForeignKey("searchspaces.id", ondelete="CASCADE"), nullable=False
|
||||
)
|
||||
search_space = relationship("SearchSpace", back_populates="reports")
|
||||
|
||||
thread_id = Column(
|
||||
Integer,
|
||||
ForeignKey("new_chat_threads.id", ondelete="SET NULL"),
|
||||
nullable=True,
|
||||
index=True,
|
||||
)
|
||||
thread = relationship("NewChatThread")
|
||||
|
||||
|
||||
class ImageGenerationConfig(BaseModel, TimestampMixin):
|
||||
"""
|
||||
Dedicated configuration table for image generation models.
|
||||
|
|
@ -1185,6 +1226,12 @@ class SearchSpace(BaseModel, TimestampMixin):
|
|||
order_by="Podcast.id.desc()",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
reports = relationship(
|
||||
"Report",
|
||||
back_populates="search_space",
|
||||
order_by="Report.id.desc()",
|
||||
cascade="all, delete-orphan",
|
||||
)
|
||||
image_generations = relationship(
|
||||
"ImageGeneration",
|
||||
back_populates="search_space",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue