Workflow-App/Workflow/scripts/_clear_ufc_documents.py
sjcoetzeesj-coder 11d0178ab1 feat: commit Workflow app source; move CIPC credentials to env
Adds the full Next.js app (app/, components/, lib/, scripts/, assets/,
docs/, Dockerfile, compose, configs) to version control.

Security:
- CIPC BizPortal customer-code logins (Login 1 + Login 3) now read
  CIPC_LOGIN1_CODE / CIPC_LOGIN1_PASSWORD / CIPC_LOGIN3_CODE /
  CIPC_LOGIN3_PASSWORD from .env.local instead of source.
- BizPortal personal login in apply-bizportal route reads
  BIZPORTAL_ID_NO / BIZPORTAL_PASSWORD.
- Added .env.example as the committed template.

.gitignore excludes .env*, node_modules, .next, logs, sqlite, Playwright
storage_state.json (live CIPC cookies), scratch dirs, client PDFs and
debug screenshots.
2026-10-18 15:21:45 +02:00

102 lines
No EOL
3.5 KiB
Python

"""Clear all three UFC documents, after saving local copies.
The user asked to remove every document from the UFC resolution:
1. ysterbaard_resolutions.signed_letter
2. ysterbaard_director_resignations.letter
3. ysterbaard_director_resignations.signature
PocketBase file deletion is irreversible, so the bytes are copied into
scripts/_backup_ufc_docs/ first. This script never deletes the record itself —
only the file fields, which is what "remove the documents" means here.
"""
import io
import json
import urllib.error
import urllib.parse
import urllib.request
from pathlib import Path
from pypdf import PdfReader
JEI = Path(r"C:\Users\SJ\.minimax-agent\projects\JEI")
WORKFLOW = Path(r"C:\Users\SJ\.minimax-agent\projects\Workflow")
BACKUP = WORKFLOW / "scripts" / "_backup_ufc_docs"
env = {}
for line in (JEI / ".env.local").read_text(encoding="utf-8").splitlines():
line = line.strip()
if line and not line.startswith("#") and "=" in line:
k, v = line.split("=", 1)
env[k.strip()] = v.strip().strip('"').strip("'")
PB = env.get("NEXT_PUBLIC_PB_URL") or env["PB_URL"]
TOK = env["PB_TOKEN"]
TARGETS = [
("pbc_266170881", "e0dbxprazb70rqx", "signed_letter", "resolution_signed_letter"),
("ysterbaard_director_resignations", "8cfie18uhgy0n0o", "letter", "resignation_letter"),
("ysterbaard_director_resignations", "8cfie18uhgy0n0o", "signature", "resignation_signature"),
]
APPLY = "--apply" in __import__("sys").argv
def req(url, method="GET", body=None, ctype="application/json"):
r = urllib.request.Request(url, data=body, method=method,
headers={"Authorization": TOK, "Content-Type": ctype})
try:
with urllib.request.urlopen(r, timeout=120) as resp:
return resp.status, resp.read()
except urllib.error.HTTPError as e:
return e.code, e.read()
BACKUP.mkdir(parents=True, exist_ok=True)
for col, rid, field, label in TARGETS:
status, raw = req(f"{PB}/api/collections/{col}/records/{rid}")
rec = json.loads(raw)
fname = rec.get(field)
if not fname:
print(f"{label}: already empty")
continue
# 1) Back the bytes up locally.
furl = f"{PB}/api/files/{col}/{rid}/{urllib.parse.quote(fname)}"
status, blob = req(furl)
if status == 200:
out = BACKUP / f"{label}_{fname}"
out.write_bytes(blob)
try:
rd = PdfReader(io.BytesIO(blob))
imgs = sum(len(p.images) for p in rd.pages)
pages = len(rd.pages)
except Exception:
imgs, pages = "?", "?"
print(f"{label}: backed up {len(blob)} B -> {out.name} (pages={pages}, signature_images={imgs})")
else:
print(f"{label}: BACKUP FAILED HTTP {status} — aborting, nothing cleared")
raise SystemExit(1)
if not APPLY:
print(f" (dry run) would clear {field} on {col}/{rid}")
continue
# 2) Clear the file field.
status, raw = req(
f"{PB}/api/collections/{col}/records/{rid}",
method="PATCH",
body=json.dumps({field: ""}).encode(),
)
if status != 200:
print(f" FAILED to clear {field}: HTTP {status} {raw[:200]}")
continue
now = json.loads(raw).get(field)
print(f" cleared {field} -> {now!r}")
if not APPLY:
print("\nDRY RUN — re-run with --apply")
else:
print("\n=== final state ===")
for col, rid, field, label in TARGETS:
_, raw = req(f"{PB}/api/collections/{col}/records/{rid}")
print(f" {label:26s} {field:15s} = {json.loads(raw).get(field)!r}")
print(f"\nlocal backups in: {BACKUP}")