from __future__ import annotations from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path from typing import Optional from sqlalchemy import ( JSON, Column, DateTime, Float, Integer, MetaData, String, Table, Text, create_engine, inspect, ) def utcnow() -> datetime: return datetime.now(timezone.utc) metadata = MetaData() proposals = Table( "proposals", metadata, Column("id", Integer, primary_key=True), Column("city", String, nullable=False), Column("title", String, nullable=False), Column("body", Text, nullable=False), Column("status", String, nullable=False, default="draft"), Column("geography", String, nullable=False, default=""), Column("tags_json", Text, nullable=False, default="[]"), Column("version", Integer, nullable=False, default=1), Column("created_at", DateTime(timezone=True), nullable=False, default=utcnow), Column("updated_at", DateTime(timezone=True), nullable=False, default=utcnow), ) proposal_versions = Table( "proposal_versions", metadata, Column("id", Integer, primary_key=True), Column("proposal_id", Integer, nullable=False), Column("version", Integer, nullable=False), Column("rationale", Text, nullable=False, default=""), Column("objections_json", Text, nullable=False, default="[]"), Column("resolution_status", String, nullable=False, default="open"), Column("created_at", DateTime(timezone=True), nullable=False, default=utcnow), ) residents = Table( "residents", metadata, Column("id", Integer, primary_key=True), Column("resident_key", String, nullable=False, unique=True), Column("geography", String, nullable=False, default=""), Column("interests_json", Text, nullable=False, default="[]"), Column("lived_experience_json", Text, nullable=False, default="[]"), Column("languages_json", Text, nullable=False, default="[]"), Column("created_at", DateTime(timezone=True), nullable=False, default=utcnow), Column("updated_at", DateTime(timezone=True), nullable=False, default=utcnow), ) comments = Table( "comments", metadata, Column("id", Integer, primary_key=True), Column("proposal_id", Integer, nullable=False), Column("resident_key", String, nullable=True), Column("channel", String, nullable=False), Column("geography", String, nullable=False, default=""), Column("language", String, nullable=False, default="und"), Column("text", Text, nullable=False), Column("metadata_json", Text, nullable=False, default="{}"), Column("created_at", DateTime(timezone=True), nullable=False, default=utcnow), ) preferences = Table( "preferences", metadata, Column("id", Integer, primary_key=True), Column("proposal_id", Integer, nullable=False), Column("resident_key", String, nullable=False), Column("score", Float, nullable=False), Column("channel", String, nullable=False), Column("created_at", DateTime(timezone=True), nullable=False, default=utcnow), ) routes = Table( "routes", metadata, Column("id", Integer, primary_key=True), Column("proposal_id", Integer, nullable=False), Column("fragment_index", Integer, nullable=False), Column("resident_key", String, nullable=False), Column("score", Float, nullable=False), Column("rationale_json", Text, nullable=False, default="[]"), Column("created_at", DateTime(timezone=True), nullable=False, default=utcnow), ) ledger_entries = Table( "ledger_entries", metadata, Column("id", Integer, primary_key=True), Column("proposal_id", Integer, nullable=False), Column("kind", String, nullable=False), Column("payload_json", Text, nullable=False), Column("created_at", DateTime(timezone=True), nullable=False, default=utcnow), ) @dataclass(frozen=True) class Database: engine: object def create_database(db_url: str): engine = create_engine(db_url, future=True) metadata.create_all(engine) return Database(engine=engine) def sqlite_path_url(path: str | Path) -> str: p = Path(path) if str(p) == ":memory:": return "sqlite+pysqlite:///:memory:" return f"sqlite+pysqlite:///{p}"