Files
home_hub/backend/alembic/versions/001_initial.py
T

163 lines
8.2 KiB
Python

"""Schémas initiaux et toutes les tables
Revision ID: 001
Revises:
Create Date: 2026-05-24
"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql
revision = "001"
down_revision = None
branch_labels = None
depends_on = None
def upgrade() -> None:
op.execute("CREATE SCHEMA IF NOT EXISTS todos")
op.execute("CREATE SCHEMA IF NOT EXISTS shopping")
op.execute("CREATE SCHEMA IF NOT EXISTS notes")
# ── todos.items ──────────────────────────────────────────────
op.create_table(
"items",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("title", sa.String(255), nullable=False),
sa.Column("body", sa.Text),
sa.Column("url", sa.Text),
sa.Column("domain", sa.String(50)),
sa.Column("category", sa.String(50)),
sa.Column("tags", postgresql.ARRAY(sa.String(50)), server_default=sa.text("'{}'::varchar[]")),
sa.Column("status", sa.String(20), server_default="pending"),
sa.Column("priority", sa.String(10), server_default="medium"),
sa.Column("due_date", sa.TIMESTAMP(timezone=True)),
sa.Column("postponed_count", sa.Integer, server_default="0"),
sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()")),
sa.Column("updated_at", sa.TIMESTAMP(timezone=True)),
sa.Column("owner_id", postgresql.UUID(as_uuid=True)),
schema="todos",
)
op.create_index("idx_todos_tags", "items", [sa.text("tags")], schema="todos", postgresql_using="gin")
# ── shopping.products ────────────────────────────────────────
op.create_table(
"products",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("name", sa.String(150), nullable=False),
sa.Column("brand", sa.String(100)),
sa.Column("category", sa.String(50)),
sa.Column("image_path", sa.String(255)),
sa.Column("thumbnail_path", sa.String(255)),
sa.Column("default_unit", sa.String(20)),
sa.Column("barcode", sa.String(50)),
sa.Column("frequency_score", sa.Integer, server_default="0"),
sa.Column("owner_id", postgresql.UUID(as_uuid=True)),
sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()")),
schema="shopping",
)
# ── shopping.stores ──────────────────────────────────────────
op.create_table(
"stores",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("name", sa.String(100), nullable=False),
sa.Column("location", sa.Text),
sa.Column("owner_id", postgresql.UUID(as_uuid=True)),
schema="shopping",
)
# ── shopping.price_history ───────────────────────────────────
op.create_table(
"price_history",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("product_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("shopping.products.id", ondelete="CASCADE"), nullable=False),
sa.Column("store_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("shopping.stores.id", ondelete="SET NULL")),
sa.Column("price", sa.Numeric(8, 2), nullable=False),
sa.Column("unit", sa.String(20)),
sa.Column("quantity", sa.Numeric(8, 3)),
sa.Column("source", sa.String(20), server_default="manual"),
sa.Column("recorded_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()")),
schema="shopping",
)
# ── shopping.lists ───────────────────────────────────────────
op.create_table(
"lists",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("name", sa.String(100)),
sa.Column("store_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("shopping.stores.id", ondelete="SET NULL")),
sa.Column("week_date", sa.Date),
sa.Column("status", sa.String(20), server_default="draft"),
sa.Column("owner_id", postgresql.UUID(as_uuid=True)),
sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()")),
schema="shopping",
)
# ── shopping.list_items ──────────────────────────────────────
op.create_table(
"list_items",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("list_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("shopping.lists.id", ondelete="CASCADE"), nullable=False),
sa.Column("product_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("shopping.products.id", ondelete="SET NULL")),
sa.Column("custom_name", sa.String(150)),
sa.Column("quantity", sa.Numeric(8, 3)),
sa.Column("unit", sa.String(20)),
sa.Column("is_checked", sa.Boolean, server_default="false"),
sa.Column("price_recorded", sa.Numeric(8, 2)),
sa.Column("carried_over", sa.Boolean, server_default="false"),
sa.Column("sort_order", sa.Integer),
schema="shopping",
)
# ── notes.items ──────────────────────────────────────────────
op.create_table(
"items",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("title", sa.String(255)),
sa.Column("content", sa.Text, nullable=False),
sa.Column("category", sa.String(50)),
sa.Column("tags", postgresql.ARRAY(sa.String(50)), server_default=sa.text("'{}'::varchar[]")),
sa.Column("gps_lat", sa.Numeric(10, 7)),
sa.Column("gps_lon", sa.Numeric(10, 7)),
sa.Column("metadata", postgresql.JSONB),
sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()")),
sa.Column("owner_id", postgresql.UUID(as_uuid=True)),
schema="notes",
)
op.create_index("idx_notes_tags", "items", [sa.text("tags")], schema="notes", postgresql_using="gin")
op.create_index(
"idx_notes_fts",
"items",
[sa.text("to_tsvector('french', coalesce(title,'') || ' ' || content)")],
schema="notes",
postgresql_using="gin",
)
# ── notes.attachments ────────────────────────────────────────
op.create_table(
"attachments",
sa.Column("id", postgresql.UUID(as_uuid=True), primary_key=True, server_default=sa.text("gen_random_uuid()")),
sa.Column("note_id", postgresql.UUID(as_uuid=True), sa.ForeignKey("notes.items.id", ondelete="CASCADE"), nullable=False),
sa.Column("file_path", sa.String(255)),
sa.Column("thumbnail_path", sa.String(255)),
sa.Column("file_type", sa.String(20)),
sa.Column("original_name", sa.String(255)),
sa.Column("created_at", sa.TIMESTAMP(timezone=True), server_default=sa.text("now()")),
schema="notes",
)
def downgrade() -> None:
op.drop_table("attachments", schema="notes")
op.drop_table("items", schema="notes")
op.drop_table("list_items", schema="shopping")
op.drop_table("lists", schema="shopping")
op.drop_table("price_history", schema="shopping")
op.drop_table("stores", schema="shopping")
op.drop_table("products", schema="shopping")
op.drop_table("items", schema="todos")
op.execute("DROP SCHEMA IF EXISTS notes CASCADE")
op.execute("DROP SCHEMA IF EXISTS shopping CASCADE")
op.execute("DROP SCHEMA IF EXISTS todos CASCADE")