yeepee-ctr / tools /package_final.py
Eric-Yyvuw's picture
sync: 2026-10-28 04:40 Dublin
3ddadd8 verified
Raw History Blame Contribute Delete
3.68 kB
#!/usr/bin/env python3
"""Test gate for final packaging (Yeepee, Huawei TechArena 2026 CTR).
A submission is zipped as FINAL only if its local test score reaches the bar
(default 0.83) on the primary cut C=25 (staleness-reweighted AUC when given,
otherwise overall AUC) AND it passes the format checks. Otherwise nothing is
written and the script exits with code 1.
Usage:
python3 package_final.py --csv sub.csv --metrics metrics.json [--name v10] [--bar 0.83]
python3 package_final.py --csv sub.csv --auc25 0.8312 [--rw25 0.8305]
metrics.json: either {"cut":25,"auc":..,"rw":..} or a list / jsonl of such rows
(the row with cut 25 is used).
"""
import argparse, hashlib, json, math, os, sys, zipfile
import pandas as pd
REF = "/workspace/huawei/data/evaluation.csv" # reference rowid set (eval set; same set as the former v2work/out/submission_v8.csv, lost in the 2026-10-28 box restore)
N_ROWS = 529390
def load_c25(path):
txt = open(path).read().strip()
rows = []
try:
j = json.loads(txt); rows = j if isinstance(j, list) else [j]
except json.JSONDecodeError:
rows = [json.loads(l) for l in txt.splitlines() if l.strip()]
c25 = [r for r in rows if int(r.get("cut", 25)) == 25]
if not c25:
sys.exit("GATE FAIL: no C=25 row in metrics file")
return c25[-1]
def num(x):
try:
x = float(x); return None if math.isnan(x) else x
except (TypeError, ValueError):
return None
def main():
ap = argparse.ArgumentParser()
ap.add_argument("--csv", required=True)
ap.add_argument("--metrics"); ap.add_argument("--auc25", type=float); ap.add_argument("--rw25", type=float)
ap.add_argument("--bar", type=float, default=0.83)
ap.add_argument("--name"); ap.add_argument("--outdir")
a = ap.parse_args()
if a.metrics:
r = load_c25(a.metrics); auc, rw = num(r.get("auc")), num(r.get("rw"))
else:
auc, rw = a.auc25, a.rw25
score = rw if rw is not None else auc
if score is None:
sys.exit("GATE FAIL: no local C=25 score given; refusing to package")
label = "C=25 reweighted AUC" if rw is not None else "C=25 AUC"
print(f"{label}: {score:.5f} (bar {a.bar})")
if score < a.bar:
print(f"GATE FAIL: {score:.5f} < {a.bar}. Not packaged."); sys.exit(1)
df = pd.read_csv(a.csv)
errs = []
if list(df.columns) != ["rowid", "predicted_probability"]: errs.append(f"columns {list(df.columns)}")
if len(df) != N_ROWS: errs.append(f"{len(df)} rows, expected {N_ROWS}")
if df["rowid"].duplicated().any(): errs.append("duplicate rowids")
p = df["predicted_probability"]
if p.isna().any() or (p < 0).any() or (p > 1).any(): errs.append("probabilities missing or outside [0,1]")
if os.path.exists(REF):
ref = pd.read_csv(REF, usecols=["rowid"])["rowid"]
if set(ref) != set(df["rowid"]): errs.append("rowid set differs from eval set")
if errs:
print("GATE FAIL (format): " + "; ".join(errs)); sys.exit(1)
name = a.name or os.path.splitext(os.path.basename(a.csv))[0]
outdir = a.outdir or os.path.dirname(os.path.abspath(a.csv))
zp = os.path.join(outdir, f"{name}_FINAL.csv.zip")
with zipfile.ZipFile(zp, "w", zipfile.ZIP_DEFLATED) as z:
z.write(a.csv, arcname=f"{name}.csv")
sha = hashlib.sha256(open(zp, "rb").read()).hexdigest()
with open(zp + ".gate.json", "w") as f:
json.dump({"csv": os.path.abspath(a.csv), "zip": zp, "sha256": sha, "score_c25": score,
"metric": label, "bar": a.bar, "auc25": auc, "rw25": rw}, f, indent=1)
print(f"GATE PASS: packaged {zp}\nsha256 {sha}")
if __name__ == "__main__":
main()