"""initial schema Revision ID: 0b9c1d2e3f4a Revises: Create Date: 2026-04-23 00:00:00.000000+00:00 Idempotent: uses CREATE TABLE IF NOT EXISTS so it is safe to run against a DB that was already bootstrapped via SQLAlchemy create_all(). """ from typing import Sequence, Union from alembic import op revision: str = '0b9c1d2e3f4a' down_revision: Union[str, None] = None branch_labels: Union[str, Sequence[str], None] = None depends_on: Union[str, Sequence[str], None] = None def upgrade() -> None: op.execute(""" CREATE TABLE IF NOT EXISTS duniter_identities ( id UUID PRIMARY KEY, address VARCHAR(64) NOT NULL, display_name VARCHAR(128), wot_status VARCHAR(32) NOT NULL DEFAULT 'unknown', is_smith BOOLEAN NOT NULL DEFAULT FALSE, is_techcomm BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_duniter_identities_address ON duniter_identities (address)") op.execute(""" CREATE TABLE IF NOT EXISTS sessions ( id UUID PRIMARY KEY, token_hash VARCHAR(128) NOT NULL, identity_id UUID NOT NULL REFERENCES duniter_identities(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), expires_at TIMESTAMPTZ NOT NULL ) """) op.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_sessions_token_hash ON sessions (token_hash)") op.execute(""" CREATE TABLE IF NOT EXISTS organizations ( id UUID PRIMARY KEY, name VARCHAR(128) NOT NULL, slug VARCHAR(128) NOT NULL, org_type VARCHAR(64) NOT NULL DEFAULT 'community', is_transparent BOOLEAN NOT NULL DEFAULT FALSE, color VARCHAR(32), icon VARCHAR(64), description TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_organizations_slug ON organizations (slug)") op.execute(""" CREATE TABLE IF NOT EXISTS org_members ( id UUID PRIMARY KEY, org_id UUID NOT NULL REFERENCES organizations(id), identity_id UUID NOT NULL REFERENCES duniter_identities(id), role VARCHAR(32) NOT NULL DEFAULT 'member', created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS formula_configs ( id UUID PRIMARY KEY, name VARCHAR(128) NOT NULL, description TEXT, duration_days INTEGER NOT NULL DEFAULT 30, majority_pct INTEGER NOT NULL DEFAULT 50, base_exponent FLOAT NOT NULL DEFAULT 0.1, gradient_exponent FLOAT NOT NULL DEFAULT 0.2, constant_base FLOAT NOT NULL DEFAULT 0.0, smith_exponent FLOAT, techcomm_exponent FLOAT, nuanced_min_participants INTEGER, nuanced_threshold_pct INTEGER, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS voting_protocols ( id UUID PRIMARY KEY, name VARCHAR(128) NOT NULL, description TEXT, vote_type VARCHAR(32) NOT NULL, formula_config_id UUID NOT NULL REFERENCES formula_configs(id), mode_params VARCHAR(64), is_meta_governed BOOLEAN NOT NULL DEFAULT FALSE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS sanctuary_entries ( id UUID PRIMARY KEY, entry_type VARCHAR(64) NOT NULL, reference_id UUID NOT NULL, title VARCHAR(256), content_hash VARCHAR(128) NOT NULL, ipfs_cid VARCHAR(128), chain_tx_hash VARCHAR(128), chain_block INTEGER, metadata_json TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS blockchain_cache ( id UUID PRIMARY KEY, cache_key VARCHAR(256) NOT NULL, cache_value JSON NOT NULL, fetched_at TIMESTAMPTZ NOT NULL DEFAULT now(), expires_at TIMESTAMPTZ NOT NULL ) """) op.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_blockchain_cache_cache_key ON blockchain_cache (cache_key)") op.execute(""" CREATE TABLE IF NOT EXISTS documents ( id UUID PRIMARY KEY, slug VARCHAR(128) NOT NULL, title VARCHAR(256) NOT NULL, doc_type VARCHAR(64) NOT NULL, version VARCHAR(32) NOT NULL DEFAULT '0.1.0', status VARCHAR(32) NOT NULL DEFAULT 'draft', description TEXT, ipfs_cid VARCHAR(128), chain_anchor VARCHAR(128), genesis_json TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute("CREATE UNIQUE INDEX IF NOT EXISTS ix_documents_slug ON documents (slug)") op.execute(""" CREATE TABLE IF NOT EXISTS decisions ( id UUID PRIMARY KEY, title VARCHAR(256) NOT NULL, description TEXT, context TEXT, decision_type VARCHAR(64) NOT NULL, status VARCHAR(32) NOT NULL DEFAULT 'draft', voting_protocol_id UUID REFERENCES voting_protocols(id), created_by_id UUID REFERENCES duniter_identities(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS item_versions ( id UUID PRIMARY KEY, item_id UUID NOT NULL, proposed_text TEXT NOT NULL, diff_text TEXT, rationale TEXT, status VARCHAR(32) NOT NULL DEFAULT 'proposed', decision_id UUID REFERENCES decisions(id), proposed_by_id UUID REFERENCES duniter_identities(id), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS vote_sessions ( id UUID PRIMARY KEY, decision_id UUID REFERENCES decisions(id), item_version_id UUID REFERENCES item_versions(id), voting_protocol_id UUID NOT NULL REFERENCES voting_protocols(id), wot_size INTEGER NOT NULL DEFAULT 0, smith_size INTEGER NOT NULL DEFAULT 0, techcomm_size INTEGER NOT NULL DEFAULT 0, starts_at TIMESTAMPTZ NOT NULL DEFAULT now(), ends_at TIMESTAMPTZ NOT NULL, status VARCHAR(32) NOT NULL DEFAULT 'open', votes_for INTEGER NOT NULL DEFAULT 0, votes_against INTEGER NOT NULL DEFAULT 0, votes_total INTEGER NOT NULL DEFAULT 0, smith_votes_for INTEGER NOT NULL DEFAULT 0, techcomm_votes_for INTEGER NOT NULL DEFAULT 0, threshold_required FLOAT NOT NULL DEFAULT 0.0, result VARCHAR(32), chain_recorded BOOLEAN NOT NULL DEFAULT FALSE, chain_tx_hash VARCHAR(128), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS decision_steps ( id UUID PRIMARY KEY, decision_id UUID NOT NULL REFERENCES decisions(id), step_order INTEGER NOT NULL, step_type VARCHAR(32) NOT NULL, title VARCHAR(256), description TEXT, status VARCHAR(32) NOT NULL DEFAULT 'pending', vote_session_id UUID REFERENCES vote_sessions(id), outcome TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS document_items ( id UUID PRIMARY KEY, document_id UUID NOT NULL REFERENCES documents(id), position VARCHAR(16) NOT NULL, item_type VARCHAR(32) NOT NULL DEFAULT 'clause', title VARCHAR(256), current_text TEXT NOT NULL, voting_protocol_id UUID REFERENCES voting_protocols(id), sort_order INTEGER NOT NULL DEFAULT 0, section_tag VARCHAR(64), inertia_preset VARCHAR(16) NOT NULL DEFAULT 'standard', is_permanent_vote BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" DO $$ BEGIN IF NOT EXISTS ( SELECT 1 FROM information_schema.table_constraints WHERE constraint_name = 'item_versions_item_id_fkey' AND table_name = 'item_versions' ) THEN ALTER TABLE item_versions ADD CONSTRAINT item_versions_item_id_fkey FOREIGN KEY (item_id) REFERENCES document_items(id); END IF; END $$ """) op.execute(""" CREATE TABLE IF NOT EXISTS votes ( id UUID PRIMARY KEY, session_id UUID NOT NULL REFERENCES vote_sessions(id), voter_id UUID NOT NULL REFERENCES duniter_identities(id), vote_value VARCHAR(32) NOT NULL, nuanced_level INTEGER, comment TEXT, signature TEXT NOT NULL, signed_payload TEXT NOT NULL, voter_wot_status VARCHAR(32) NOT NULL DEFAULT 'member', voter_is_smith BOOLEAN NOT NULL DEFAULT FALSE, voter_is_techcomm BOOLEAN NOT NULL DEFAULT FALSE, is_active BOOLEAN NOT NULL DEFAULT TRUE, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS mandates ( id UUID PRIMARY KEY, title VARCHAR(256) NOT NULL, description TEXT, mandate_type VARCHAR(64) NOT NULL, status VARCHAR(32) NOT NULL DEFAULT 'draft', mandatee_id UUID REFERENCES duniter_identities(id), decision_id UUID REFERENCES decisions(id), starts_at TIMESTAMPTZ, ends_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL DEFAULT now(), updated_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) op.execute(""" CREATE TABLE IF NOT EXISTS mandate_steps ( id UUID PRIMARY KEY, mandate_id UUID NOT NULL REFERENCES mandates(id), step_order INTEGER NOT NULL, step_type VARCHAR(32) NOT NULL, title VARCHAR(256), description TEXT, status VARCHAR(32) NOT NULL DEFAULT 'pending', vote_session_id UUID REFERENCES vote_sessions(id), outcome TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT now() ) """) def downgrade() -> None: # Intentionally left empty — dropping the initial schema would destroy all data. pass