dodil/erp-brick-purchasing

DODIL ERP brick: purchasing

scripts/gen_schema.py

"""Write schema.sql and migrations/00001_init/{up,down}.sql from the column model in infrastructure/tables.py (one model, so they cannot drift).

    python scripts/gen_schema.py

Additive only after a release: a new column goes at the END of its CREATE and as an explicit ALTER in a new migrations/NNNNN_<name>/ — never
regenerate 00001 once a bucket has applied it."""
import pathlib
import sys

from sqlalchemy.dialects import postgresql

ROOT = pathlib.Path(__file__).resolve().parents[1]
sys.path.insert(0, str(ROOT / "src"))
from brick_purchasing.infrastructure.tables import md  # noqa: E402

PKG = ROOT / "src" / "brick_purchasing"
HEAD = ("-- Tables of the purchasing brick (module `purchasing`). Every table prefixed `pur_`, a natural primary key each (never SERIAL), money NUMERIC(18,2),\n"
        "-- a price or rate NUMERIC(19,4), a quantity NUMERIC(19,6), the stamps last so a later column is an append. Additive only.\n"
        "-- Generated by scripts/gen_schema.py from infrastructure/tables.py.\n")


def create(t) -> str:
    d = postgresql.dialect()
    cols = [f"    {c.name} {c.type.compile(dialect=d).replace(' WITHOUT TIME ZONE', '')}{' NOT NULL' if c.primary_key else ''}," for c in t.columns]
    pk = ", ".join(c.name for c in t.primary_key.columns)
    return f"CREATE TABLE IF NOT EXISTS {t.name} (\n" + "\n".join(cols) + f"\n    PRIMARY KEY ({pk})\n);\n"


tables = sorted(md.tables.values(), key=lambda t: t.name)
body = "\n".join(create(t) for t in tables)
(PKG / "schema.sql").write_text(HEAD + "\n" + body)
mig = PKG / "migrations" / "00001_init"
if mig.exists():                                   # released: 00001 is history, only schema.sql (the current shape) is rewritten
    print(f"{len(tables)} tables (schema.sql only; 00001_init kept)")
    raise SystemExit(0)
mig.mkdir(parents=True, exist_ok=True)
(mig / "up.sql").write_text("-- 00001_init: the brick's first schema (the versioned layout migrations/NNNNN_<name>/{up,down}.sql, a per-brick journal pur_schema_migrations).\n"
                            + HEAD + "\n" + body)
(mig / "down.sql").write_text("-- 00001_init down: drops every table the brick made. Destroys the brick's data: a rollback of a first install only.\n"
                              + "".join(f"DROP TABLE IF EXISTS {t.name};\n" for t in reversed(tables)))
print(f"{len(tables)} tables")