Spaces:
Runtime error
Runtime error
Refactor VideoTemplateEditor component: remove AI editing features and related state management, streamline template loading, and update styles for improved layout. Enhance CSS for responsive design and visual consistency across various components, including dropdowns, sync stats, and quiz elements. Remove deprecated premium badge styles and adjust layout for chat components.
66de9c8 Download backend/alembic/versions/0006_plans.py from firebird-technologies/NoteStack-Test: direct link, hf CLI and curl.
- Browser
- Download file 4.29 kB
-
https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/main/backend/alembic/versions/0006_plans.py
- Command line
-
hf download hf://spaces/firebird-technologies/NoteStack-Test/backend/alembic/versions/0006_plans.py
-
curl -L -o 0006_plans.py https://huggingface.co/spaces/firebird-technologies/NoteStack-Test/resolve/main/backend/alembic/versions/0006_plans.py
4.29 kB
| """plans: one row per plan (limits, price, pricing-page copy), editable without a deploy | |
| Seeded with the plans that were hard-coded in app/services/plans.py. Rows that already exist are left alone. | |
| Revision ID: 0006_plans | |
| Revises: 0005_video_quota | |
| Create Date: 2026-09-29 | |
| """ | |
| from datetime import UTC, datetime | |
| import sqlalchemy as sa | |
| from alembic import op | |
| revision = "0006_plans" | |
| down_revision = "0005_video_quota" | |
| branch_labels = None | |
| depends_on = None | |
| SEED = [ | |
| {"id": "free", "sort_order": 0, "name": "Free", "tagline": "For trying Notestack on your archive", | |
| "price_monthly_usd": 0, "sources": 1, "indexed_posts": 5, "audio_minutes": 3, "videos": 1, "launch_kits": 2, | |
| "voice_cloning": False, "brand_kit": False, | |
| "features": ["1 source, your latest 5 posts indexed", "Grounded research chat with citations", | |
| "3 min of audio overviews a month", "1 video a month", "2 Launch Kits a month"]}, | |
| {"id": "writer", "sort_order": 1, "name": "Writer", "tagline": "For writers publishing every week", | |
| "price_monthly_usd": 24.99, "sources": 3, "indexed_posts": 500, "audio_minutes": 60, "videos": 10, | |
| "launch_kits": 50, "voice_cloning": True, "brand_kit": True, | |
| "features": ["3 sources, 500 indexed posts", "60 min of audio overviews a month", "10 videos a month", | |
| "50 Launch Kits a month", "Voice cloning with consent", "Your brand colors and logo"]}, | |
| {"id": "studio", "sort_order": 2, "name": "Studio", "tagline": "For publications and power users", | |
| "price_monthly_usd": 48.99, "sources": 10, "indexed_posts": 5000, "audio_minutes": 240, "videos": 30, | |
| "launch_kits": -1, "voice_cloning": True, "brand_kit": True, | |
| "features": ["10 sources, 5,000 indexed posts", "240 min of audio overviews a month", "30 videos a month", | |
| "Unlimited Launch Kits", "Launchpad calendar and resurfacing", "Priority rendering"]}, | |
| ] | |
| def upgrade() -> None: | |
| bind = op.get_bind() | |
| # 0001 builds from the live models, so a fresh database may already have the table (empty). | |
| if "plans" not in sa.inspect(bind).get_table_names(): | |
| op.create_table( | |
| "plans", | |
| sa.Column("id", sa.String(20), primary_key=True), | |
| sa.Column("sort_order", sa.Integer(), nullable=False, server_default="0"), | |
| sa.Column("name", sa.String(50), nullable=False), | |
| sa.Column("tagline", sa.String(200), nullable=False, server_default=""), | |
| sa.Column("price_monthly_usd", sa.Float(), nullable=False, server_default="0"), | |
| sa.Column("sources", sa.Integer(), nullable=False, server_default="1"), | |
| sa.Column("indexed_posts", sa.Integer(), nullable=False, server_default="5"), | |
| sa.Column("audio_minutes", sa.Integer(), nullable=False, server_default="0"), | |
| sa.Column("videos", sa.Integer(), nullable=False, server_default="0"), | |
| sa.Column("launch_kits", sa.Integer(), nullable=False, server_default="0"), | |
| sa.Column("voice_cloning", sa.Boolean(), nullable=False, server_default=sa.false()), | |
| sa.Column("brand_kit", sa.Boolean(), nullable=False, server_default=sa.false()), | |
| sa.Column("features", sa.JSON(), nullable=False), | |
| sa.Column("created_at", sa.DateTime(timezone=True), nullable=False), | |
| sa.Column("updated_at", sa.DateTime(timezone=True), nullable=False), | |
| ) | |
| # Columns a later migration drops (brand_kit) are not in a table 0001 built from today's models: seed what exists. | |
| have = {c["name"] for c in sa.inspect(bind).get_columns("plans")} | |
| seed = [{k: v for k, v in p.items() if k in have} for p in SEED] | |
| types = {"features": sa.JSON(), "voice_cloning": sa.Boolean(), "brand_kit": sa.Boolean(), | |
| "created_at": sa.DateTime(timezone=True), "updated_at": sa.DateTime(timezone=True)} | |
| plans = sa.table("plans", *(sa.column(k, types.get(k)) for k in [*seed[0], "created_at", "updated_at"])) | |
| existing = {row[0] for row in bind.execute(sa.text("SELECT id FROM plans"))} | |
| now = datetime.now(UTC) | |
| missing = [{**p, "created_at": now, "updated_at": now} for p in seed if p["id"] not in existing] | |
| if missing: | |
| op.bulk_insert(plans, missing) | |
| def downgrade() -> None: | |
| op.drop_table("plans") | |