SPB Git

spb/wp42_uqo

Public
3.6 KB · 60 lines python
1#!/usr/bin/env python32"""PUBLIC self-check (arm A4 and controller): verifies deliverable presence and well-formedness only. It knows nothing about the3correct answers. Usage: python tools/selfcheck.py [--workspace .] [--json]"""4from __future__ import annotations5import argparse, json, os, sys, zipfile67def check(workspace: str) -> dict:8    task = json.load(open(os.path.join(workspace, "task.json"), encoding="utf-8"))9    out = os.path.join(workspace, "out"); problems = []; checks = []10    for d in task.get("required_deliverables", []):11        p = os.path.join(out, d["path"]); ok = os.path.isfile(p) and os.path.getsize(p) > 012        checks.append({"path": d["path"], "exists": ok})13        if not ok: problems.append(f"missing or empty deliverable: out/{d['path']}"); continue14        kind = d.get("kind") or os.path.splitext(p)[1].lstrip(".")15        if kind in ("xlsx", "docx", "pptx"):16            try:17                with zipfile.ZipFile(p) as z: z.testzip()18            except Exception as e:  # noqa: BLE00119                problems.append(f"out/{d['path']} is not a valid {kind} (zip) file: {e}"); continue20            if kind == "xlsx" and d.get("must_have_formulas"):21                try:22                    import openpyxl23                    wb = openpyxl.load_workbook(p); n = sum(1 for ws in wb.worksheets for row in ws.iter_rows() for c in row if isinstance(c.value, str) and c.value.startswith("="))24                    if n == 0: problems.append(f"out/{d['path']} contains no formulas (the brief requires a working spreadsheet)")25                    errs = 026                    try:27                        wb2 = openpyxl.load_workbook(p, data_only=True)28                        errs = sum(1 for ws in wb2.worksheets for row in ws.iter_rows() for c in row if isinstance(c.value, str) and c.value.startswith("#"))29                    except Exception: pass  # noqa: BLE00130                    if errs: problems.append(f"out/{d['path']} has {errs} cells with error values (#REF!, #DIV/0!, ...)")31                except ImportError: pass32            if kind == "docx":33                try:34                    import docx35                    n = sum(len(par.text.split()) for par in docx.Document(p).paragraphs)36                    if n < 50: problems.append(f"out/{d['path']} has only {n} words")37                except ImportError: pass38        if kind == "json":39            try: json.load(open(p, encoding="utf-8"))40            except Exception as e: problems.append(f"out/{d['path']} is not valid JSON: {e}")  # noqa: BLE00141    cp = os.path.join(out, "COMPLETION.json")42    if os.path.isfile(cp):43        try:44            c = json.load(open(cp, encoding="utf-8"))45            for k in ("status", "summary", "deliverables"):46                if k not in c: problems.append(f"COMPLETION.json lacks '{k}'")47            if c.get("status") not in ("completed", "needs_clarification", "abstained"): problems.append("COMPLETION.json status must be completed | needs_clarification | abstained")48        except Exception as e: problems.append(f"COMPLETION.json invalid: {e}")  # noqa: BLE00149    else: problems.append("missing out/COMPLETION.json")50    return {"ok": not problems, "problems": problems, "checks": checks}5152if __name__ == "__main__":53    ap = argparse.ArgumentParser(); ap.add_argument("--workspace", default="."); ap.add_argument("--json", action="store_true")54    a = ap.parse_args(); r = check(a.workspace)55    if a.json: print(json.dumps(r, indent=1))56    else:57        print("SELF-CHECK:", "OK" if r["ok"] else f"{len(r['problems'])} problem(s)")58        for p in r["problems"]: print(" -", p)59    sys.exit(0 if r["ok"] else 1)60
Files · 3fda4556ac5785ac06c1a250f7b7ace53dfe6763
Menu

Theme