149 lines
4.9 KiB
Python
149 lines
4.9 KiB
Python
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,
|
|
)
|
|
from sqlalchemy.engine import Engine
|
|
|
|
|
|
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),
|
|
)
|
|
|
|
capture_batches = Table(
|
|
"capture_batches",
|
|
metadata,
|
|
Column("id", Integer, primary_key=True),
|
|
Column("batch_key", String, nullable=False, unique=True),
|
|
Column("source", String, nullable=False),
|
|
Column("captured_at", DateTime(timezone=True), nullable=False, default=utcnow),
|
|
Column("item_count", Integer, nullable=False, default=0),
|
|
Column("status", String, nullable=False, default="received"),
|
|
Column("payload_json", Text, nullable=False, default="{}"),
|
|
Column("results_json", Text, nullable=False, default="[]"),
|
|
Column("created_at", DateTime(timezone=True), nullable=False, default=utcnow),
|
|
Column("ingested_at", DateTime(timezone=True), nullable=True),
|
|
)
|
|
|
|
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: Engine
|
|
|
|
|
|
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}"
|