116 lines
4.9 KiB
Python
116 lines
4.9 KiB
Python
"""CLI: пакетная генерация документов без веб-сервера.
|
|
|
|
Примеры:
|
|
python -m certgen.gen --template classic --fio "Иванов Иван Иванович" \
|
|
--body "за успешное прохождение курса"
|
|
|
|
python -m certgen.gen --template letter --csv names.csv --outdir out
|
|
|
|
В CSV первая строка может быть заголовком (fio,title,body,date,prefix);
|
|
допускаются разделители ',' и ';'. Если колонок нет — берётся первый столбец как ФИО.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
import csv
|
|
import json
|
|
import re
|
|
import shutil
|
|
from pathlib import Path
|
|
|
|
from . import config, store
|
|
from .service import create_certificate
|
|
|
|
|
|
def _read_csv(path: Path) -> list[dict]:
|
|
text = path.read_text(encoding="utf-8-sig")
|
|
first = text.splitlines()[0] if text.strip() else ""
|
|
delim = ";" if first.count(";") > first.count(",") else ","
|
|
rows = list(csv.reader(text.splitlines(), delimiter=delim))
|
|
rows = [r for r in rows if any(c.strip() for c in r)]
|
|
if not rows:
|
|
return []
|
|
header = [c.strip().lower() for c in rows[0]]
|
|
known = {"fio", "фио", "title", "заголовок", "body", "текст",
|
|
"date", "дата", "prefix", "префикс", "number", "номер", "email", "почта"}
|
|
has_header = bool(known & set(header))
|
|
records = []
|
|
data = rows[1:] if has_header else rows
|
|
keys = header if has_header else ["fio"]
|
|
for row in data:
|
|
rec = {}
|
|
if has_header:
|
|
for i, key in enumerate(keys):
|
|
rec[key] = row[i] if i < len(row) else ""
|
|
else:
|
|
rec["fio"] = row[0]
|
|
# нормализуем русские заголовки
|
|
for ru, en in (("фио", "fio"), ("заголовок", "title"), ("текст", "body"),
|
|
("дата", "date"), ("префикс", "prefix"), ("номер", "number"),
|
|
("почта", "email")):
|
|
if ru in rec and en not in rec:
|
|
rec[en] = rec.pop(ru)
|
|
records.append(rec)
|
|
return records
|
|
|
|
|
|
def main() -> None:
|
|
p = argparse.ArgumentParser(prog="certgen.gen", description="Пакетная генерация документов")
|
|
p.add_argument("--template", default="classic", help="slug шаблона")
|
|
p.add_argument("--fio", help="ФИО для одного документа")
|
|
p.add_argument("--email", help="email получателя документа")
|
|
p.add_argument("--title", help="заголовок")
|
|
p.add_argument("--body", help="дополнительный текст")
|
|
p.add_argument("--date", help="дата")
|
|
p.add_argument("--prefix", help="префикс номера")
|
|
p.add_argument("--number", help="особый номер из резерва 1..(START-1)")
|
|
p.add_argument("--csv", type=Path, help="CSV со списком")
|
|
p.add_argument("--names", help="список ФИО через ';'")
|
|
p.add_argument("--outdir", type=Path, help="куда скопировать готовые файлы")
|
|
p.add_argument("--base", help="базовый URL для ссылок")
|
|
p.add_argument("--json", action="store_true", help="вывести результат как JSON")
|
|
args = p.parse_args()
|
|
|
|
config.ensure_dirs()
|
|
store.init_db()
|
|
template = store.get_template(args.template)
|
|
if not template:
|
|
raise SystemExit(f"Неизвестный шаблон: {args.template}. "
|
|
f"Доступны: {[t['slug'] for t in store.list_templates()]}")
|
|
|
|
records: list[dict] = []
|
|
if args.csv:
|
|
records = _read_csv(args.csv)
|
|
elif args.names:
|
|
records = [{"fio": n.strip()} for n in args.names.split(";") if n.strip()]
|
|
elif args.fio:
|
|
records = [{"fio": args.fio}]
|
|
else:
|
|
raise SystemExit("Укажите --fio, --names или --csv")
|
|
|
|
results = []
|
|
for rec in records:
|
|
body = {"fio": rec.get("fio", "")}
|
|
for key, val in (("title", args.title), ("body", args.body),
|
|
("date", args.date), ("prefix", args.prefix),
|
|
("number", args.number), ("email", args.email)):
|
|
value = rec.get(key) or val
|
|
if value:
|
|
body[key] = value
|
|
cert = create_certificate(template, body, base=args.base)
|
|
if args.outdir:
|
|
args.outdir.mkdir(parents=True, exist_ok=True)
|
|
safe = re.sub(r"[^\w.-]+", "_", cert["number"]) or str(cert["seq"])
|
|
shutil.copyfile(cert["png_path"], args.outdir / f"{safe}.png")
|
|
shutil.copyfile(cert["pdf_path"], args.outdir / f"{safe}.pdf")
|
|
results.append(cert)
|
|
if not args.json:
|
|
print(f"№ {cert['number']} {cert['fio']} -> {cert['url']}")
|
|
|
|
if args.json:
|
|
print(json.dumps(results, ensure_ascii=False, indent=2))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|