idea198-civicswarm-privacy-.../civicswarm/db.py

133 lines
4.2 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,
)
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}"