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")