"""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")