"""Verify a fixed Ultimate Big Board capture. Python 3.10+, standard library only.""" import argparse import csv import hashlib import io import json import re from datetime import datetime from urllib.parse import urlsplit from urllib.request import HTTPRedirectHandler, Request, build_opener ORIGIN = "https://ultimatebigboard.com" CATALOG_URL = ORIGIN + "/captures/catalog.json" MAX_BYTES = 2_000_000 FIELDS = "Rank Player Position School DraftYear League Snapshot SnapshotGeneratedAt CapturedAt BoardURL MethodologyURL ProfileURL CalculationVersion EligibleMocks".split() CAPTURE_PATH = re.compile(r"/captures/(nfl|nba|wnba)/(2027|2028)/(\d{8}T\d{6}Z)-([a-f0-9]{12})/") def fixed_url(url, csv_file=False): parts = urlsplit(url) suffix = "rankings.csv" if csv_file else "" path = parts.path[:-len(suffix)] if suffix and parts.path.endswith(suffix) else parts.path match = CAPTURE_PATH.fullmatch(path) if (parts.scheme != "https" or parts.netloc != "ultimatebigboard.com" or parts.query or parts.fragment or not match or (csv_file and not parts.path.endswith(suffix))): raise ValueError("Expected a fixed HTTPS capture URL on ultimatebigboard.com") return match def timestamp(value): if not isinstance(value, str): raise ValueError("Timestamp must be text") parsed = datetime.fromisoformat(value.replace("Z", "+00:00")) if parsed.tzinfo is None: raise ValueError("Timestamp needs an explicit timezone") return parsed def verify_capture(catalog, capture_url, csv_bytes): """Validate catalog binding, file integrity and every row; return entry, rows.""" location = fixed_url(capture_url) if not isinstance(catalog, dict) or type(catalog.get("version")) is not int or catalog["version"] != 1: raise ValueError("Unsupported capture catalog") entries = catalog.get("captures") if not isinstance(entries, list) or not 1 <= len(entries) <= 1000: raise ValueError("Invalid capture catalog entries") matches = [e for e in entries if isinstance(e, dict) and e.get("url") == capture_url] if len(matches) != 1: raise ValueError("Capture must have exactly one catalog entry") entry = matches[0] fixed_url(entry.get("csvUrl", ""), csv_file=True) if entry["csvUrl"] != capture_url + "rankings.csv": raise ValueError("CSV URL does not belong to this capture") league, year, _, digest_prefix = location.groups() if entry.get("league") != league or type(entry.get("year")) is not int or entry["year"] != int(year): raise ValueError("Catalog league/year mismatch") digest = entry.get("sha256", "") if not isinstance(digest, str) or not re.fullmatch(r"[a-f0-9]{64}", digest) or not digest.startswith(digest_prefix): raise ValueError("Invalid catalog checksum or capture path") if not isinstance(csv_bytes, bytes) or len(csv_bytes) > MAX_BYTES or hashlib.sha256(csv_bytes).hexdigest() != digest: raise ValueError("CSV checksum mismatch or file too large") if type(entry.get("rows")) is not int or not 1 <= entry["rows"] <= 5000: raise ValueError("Invalid row count") if type(entry.get("eligibleMocks")) is not int or entry["eligibleMocks"] < 1: raise ValueError("Invalid eligible-mock count") captured = timestamp(entry.get("capturedAt")) if captured.strftime("%Y%m%dT%H%M%SZ") != location.group(3) or captured.utcoffset().total_seconds() != 0: raise ValueError("Capture path/time mismatch") snapshot = entry.get("snapshot") version = entry.get("calculationVersion") if not isinstance(snapshot, str) or not snapshot or snapshot in ["0", "legacy-snapshot"]: raise ValueError("Missing usable snapshot") if not isinstance(version, str) or not version or version.startswith("prototype"): raise ValueError("Missing usable calculation version") reader = csv.DictReader(io.StringIO(csv_bytes.decode("utf-8"), newline="")) if reader.fieldnames != FIELDS: raise ValueError("Unexpected CSV schema") rows = list(reader) if len(rows) != entry["rows"]: raise ValueError("CSV row count differs from catalog") board = ORIGIN + f"/{league}/{year}/" expected = {"League": league.upper(), "DraftYear": year, "Snapshot": snapshot, "CapturedAt": entry["capturedAt"], "CalculationVersion": version, "EligibleMocks": str(entry["eligibleMocks"]), "BoardURL": board + "big-board/", "MethodologyURL": board + "methodology/"} ranks, profiles, generated_values = set(), set(), set() last_rank = 0 for row in rows: if None in row or any(row.get(k) is None for k in FIELDS): raise ValueError("Malformed CSV row") if any(row[k] != value for k, value in expected.items()): raise ValueError("CSV scope or snapshot metadata mismatch") if not re.fullmatch(r"[1-9][0-9]*", row["Rank"]): raise ValueError("Invalid rank") rank = int(row["Rank"]) profile = row["ProfileURL"] if (rank in ranks or rank <= last_rank or profile in profiles or not row["Player"].strip() or not re.fullmatch(re.escape(board) + r"players/[a-z0-9]+(?:-[a-z0-9]+)*/", profile)): raise ValueError("Invalid ranking order, player or profile identity") generated = timestamp(row["SnapshotGeneratedAt"]) if generated > captured: raise ValueError("Snapshot cannot be generated after its capture") generated_values.add(row["SnapshotGeneratedAt"]) ranks.add(rank) profiles.add(profile) last_rank = rank if len(generated_values) != 1: raise ValueError("Inconsistent snapshot generation timestamps") return entry, rows class NoRedirect(HTTPRedirectHandler): def redirect_request(self, req, fp, code, msg, headers, newurl): raise ValueError("Unexpected redirect; inspect the canonical URL before retrying") def download(url): if url != CATALOG_URL: fixed_url(url, csv_file=True) request = Request(url, headers={"User-Agent": "UBB-CaptureExample/1.0 (+https://ultimatebigboard.com/data-guide/)"}) with build_opener(NoRedirect).open(request, timeout=20) as response: if response.status != 200: raise ValueError("Download did not return HTTP 200") data = response.read(MAX_BYTES + 1) if len(data) > MAX_BYTES: raise ValueError("Download exceeded the size limit") return data def main(): parser = argparse.ArgumentParser(description=__doc__) parser.add_argument("--capture-url", required=True, help="Fixed capture page URL, including trailing slash") args = parser.parse_args() fixed_url(args.capture_url) catalog = json.loads(download(CATALOG_URL)) entry, rows = verify_capture(catalog, args.capture_url, download(args.capture_url + "rankings.csv")) print(json.dumps({"verified": True, "capture": entry["url"], "sha256": entry["sha256"], "league": entry["league"], "year": entry["year"], "snapshot": entry["snapshot"], "capturedAt": entry["capturedAt"], "eligibleMocks": entry["eligibleMocks"], "rows": len(rows)}, indent=2)) print("\nFirst five published ranks (not actual draft results):") for row in rows[:5]: print(f"{row['Rank']:>3} {row['Player']} {row['Position']} {row['School']}") if __name__ == "__main__": try: main() except (ValueError, OSError, KeyError, TypeError) as error: raise SystemExit(f"Capture not verified: {error}")