Create 20260503_0016_add_choose_your_own_adventure.py
This commit is contained in:
parent
4ea67fda13
commit
24dd4e7053
1 changed files with 208 additions and 0 deletions
|
|
@ -0,0 +1,208 @@
|
||||||
|
"""add choose_your_own_adventure tables
|
||||||
|
|
||||||
|
Revision ID: 0016
|
||||||
|
Revises: 0015
|
||||||
|
Create Date: 2026-05-03
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
revision: str = "0016"
|
||||||
|
down_revision: Union[str, None] = "0015"
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
op.create_table(
|
||||||
|
"choose_your_own_adventure",
|
||||||
|
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||||
|
sa.Column(
|
||||||
|
"user_id",
|
||||||
|
postgresql.UUID(as_uuid=True),
|
||||||
|
sa.ForeignKey("users.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column("status", sa.Text(), nullable=False, server_default="awaiting_first_entry"),
|
||||||
|
sa.Column("language", sa.Text(), nullable=False),
|
||||||
|
sa.Column("source_language", sa.Text(), nullable=False),
|
||||||
|
sa.Column("competencies", postgresql.JSONB(), nullable=False, server_default="[]"),
|
||||||
|
sa.Column("max_entry_count", sa.Integer(), nullable=False, server_default="6"),
|
||||||
|
sa.Column(
|
||||||
|
"entry_story_text_target_length",
|
||||||
|
postgresql.JSONB(),
|
||||||
|
nullable=False,
|
||||||
|
server_default='{"min": 700, "max": 800}',
|
||||||
|
),
|
||||||
|
sa.Column("title", sa.Text(), nullable=False, server_default="Untitled adventure"),
|
||||||
|
sa.Column("description", sa.Text(), nullable=True),
|
||||||
|
sa.Column("plot_summary", sa.Text(), nullable=True),
|
||||||
|
sa.Column("genres", postgresql.JSONB(), nullable=False, server_default="[]"),
|
||||||
|
sa.Column("setting", postgresql.JSONB(), nullable=False, server_default="[]"),
|
||||||
|
sa.Column("vibes", postgresql.JSONB(), nullable=False, server_default="[]"),
|
||||||
|
sa.Column("protagonist", postgresql.JSONB(), nullable=False, server_default="[]"),
|
||||||
|
sa.Column(
|
||||||
|
"created_at",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
nullable=False,
|
||||||
|
server_default=sa.func.now(),
|
||||||
|
),
|
||||||
|
sa.Column("deleted_at", sa.DateTime(timezone=True), nullable=True),
|
||||||
|
)
|
||||||
|
op.create_index("ix_cyoa_user_id", "choose_your_own_adventure", ["user_id"])
|
||||||
|
op.create_index("ix_cyoa_status", "choose_your_own_adventure", ["status"])
|
||||||
|
|
||||||
|
# Entry table — generated_from_choice_id FK added after possible_choice table is created
|
||||||
|
op.create_table(
|
||||||
|
"choose_your_own_adventure_entry",
|
||||||
|
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||||
|
sa.Column(
|
||||||
|
"adventure_id",
|
||||||
|
postgresql.UUID(as_uuid=True),
|
||||||
|
sa.ForeignKey("choose_your_own_adventure.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column("generated_from_choice_id", postgresql.UUID(as_uuid=True), nullable=True),
|
||||||
|
sa.Column("status", sa.Text(), nullable=False, server_default="generating"),
|
||||||
|
sa.Column("entry_index", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("story_text", sa.Text(), nullable=True),
|
||||||
|
sa.Column("gamemaster_notes", sa.Text(), nullable=True),
|
||||||
|
sa.Column("llm_data", postgresql.JSONB(), nullable=True),
|
||||||
|
sa.Column(
|
||||||
|
"created_at",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
nullable=False,
|
||||||
|
server_default=sa.func.now(),
|
||||||
|
),
|
||||||
|
sa.UniqueConstraint("adventure_id", "entry_index", name="uq_cyoa_entry_adventure_index"),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_cyoa_entry_adventure_id", "choose_your_own_adventure_entry", ["adventure_id"]
|
||||||
|
)
|
||||||
|
|
||||||
|
op.create_table(
|
||||||
|
"choose_your_own_adventure_entry_possible_choice",
|
||||||
|
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||||
|
sa.Column(
|
||||||
|
"entry_id",
|
||||||
|
postgresql.UUID(as_uuid=True),
|
||||||
|
sa.ForeignKey("choose_your_own_adventure_entry.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column("index", sa.Integer(), nullable=False),
|
||||||
|
sa.Column("label", sa.Text(), nullable=False),
|
||||||
|
sa.Column("text", sa.Text(), nullable=False),
|
||||||
|
sa.UniqueConstraint("entry_id", "index", name="uq_cyoa_choice_entry_index"),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_cyoa_choice_entry_id",
|
||||||
|
"choose_your_own_adventure_entry_possible_choice",
|
||||||
|
["entry_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
# Resolve circular FK: entry → possible_choice
|
||||||
|
op.create_foreign_key(
|
||||||
|
"fk_cyoa_entry_generated_from_choice",
|
||||||
|
"choose_your_own_adventure_entry",
|
||||||
|
"choose_your_own_adventure_entry_possible_choice",
|
||||||
|
["generated_from_choice_id"],
|
||||||
|
["id"],
|
||||||
|
ondelete="SET NULL",
|
||||||
|
)
|
||||||
|
|
||||||
|
op.create_table(
|
||||||
|
"choose_your_own_adventure_entry_possible_choice_decision",
|
||||||
|
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||||
|
sa.Column(
|
||||||
|
"choice_id",
|
||||||
|
postgresql.UUID(as_uuid=True),
|
||||||
|
sa.ForeignKey(
|
||||||
|
"choose_your_own_adventure_entry_possible_choice.id", ondelete="CASCADE"
|
||||||
|
),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
"user_id",
|
||||||
|
postgresql.UUID(as_uuid=True),
|
||||||
|
sa.ForeignKey("users.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column(
|
||||||
|
"created_at",
|
||||||
|
sa.DateTime(timezone=True),
|
||||||
|
nullable=False,
|
||||||
|
server_default=sa.func.now(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_cyoa_decision_choice_id",
|
||||||
|
"choose_your_own_adventure_entry_possible_choice_decision",
|
||||||
|
["choice_id"],
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_cyoa_decision_user_id",
|
||||||
|
"choose_your_own_adventure_entry_possible_choice_decision",
|
||||||
|
["user_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
op.create_table(
|
||||||
|
"choose_your_own_adventure_entry_translation",
|
||||||
|
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||||
|
sa.Column(
|
||||||
|
"entry_id",
|
||||||
|
postgresql.UUID(as_uuid=True),
|
||||||
|
sa.ForeignKey("choose_your_own_adventure_entry.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column("component_type", sa.Text(), nullable=False, server_default="story_text"),
|
||||||
|
sa.Column("target_language", sa.Text(), nullable=False),
|
||||||
|
sa.Column("translated_text", sa.Text(), nullable=False),
|
||||||
|
sa.UniqueConstraint(
|
||||||
|
"entry_id", "component_type", "target_language",
|
||||||
|
name="uq_cyoa_translation_entry_component_lang",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_cyoa_translation_entry_id",
|
||||||
|
"choose_your_own_adventure_entry_translation",
|
||||||
|
["entry_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
op.create_table(
|
||||||
|
"choose_your_own_adventure_entry_audio",
|
||||||
|
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True),
|
||||||
|
sa.Column(
|
||||||
|
"entry_id",
|
||||||
|
postgresql.UUID(as_uuid=True),
|
||||||
|
sa.ForeignKey("choose_your_own_adventure_entry.id", ondelete="CASCADE"),
|
||||||
|
nullable=False,
|
||||||
|
),
|
||||||
|
sa.Column("component_type", sa.Text(), nullable=False, server_default="story_text"),
|
||||||
|
sa.Column("tts_provider", sa.Text(), nullable=False, server_default="google_gemini"),
|
||||||
|
sa.Column("tts_options", postgresql.JSONB(), nullable=True),
|
||||||
|
sa.Column("file_name", sa.Text(), nullable=False),
|
||||||
|
sa.UniqueConstraint("entry_id", "component_type", name="uq_cyoa_audio_entry_component"),
|
||||||
|
)
|
||||||
|
op.create_index(
|
||||||
|
"ix_cyoa_audio_entry_id",
|
||||||
|
"choose_your_own_adventure_entry_audio",
|
||||||
|
["entry_id"],
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
op.drop_table("choose_your_own_adventure_entry_audio")
|
||||||
|
op.drop_table("choose_your_own_adventure_entry_translation")
|
||||||
|
op.drop_table("choose_your_own_adventure_entry_possible_choice_decision")
|
||||||
|
op.drop_constraint(
|
||||||
|
"fk_cyoa_entry_generated_from_choice",
|
||||||
|
"choose_your_own_adventure_entry",
|
||||||
|
type_="foreignkey",
|
||||||
|
)
|
||||||
|
op.drop_table("choose_your_own_adventure_entry_possible_choice")
|
||||||
|
op.drop_table("choose_your_own_adventure_entry")
|
||||||
|
op.drop_table("choose_your_own_adventure")
|
||||||
Loading…
Reference in a new issue