| from __future__ import annotations |
|
|
| import argparse |
| import json |
| import os |
| import subprocess |
| import sys |
| from datetime import UTC, datetime |
| from pathlib import Path |
| from typing import Any |
| from urllib.error import HTTPError, URLError |
| from urllib.request import Request, urlopen |
|
|
| from check_public_space import DEFAULT_SPACE, run_checks |
| from huggingface_hub import HfApi |
|
|
| DEFAULT_MODEL_REPO = "build-small-hackathon/dota2tuned-qwen3-4b-2507-lora" |
| DEFAULT_DATASET_REPO = "build-small-hackathon/dota2tuned-data" |
| DEFAULT_MODAL_URL = "https://dracufeuer--dota2tuned-ui.modal.run" |
| DEFAULT_GITHUB_REPO = "https://github.com/1ncompleteness/DOTA2Tuned" |
|
|
| MANUAL_ITEMS = [ |
| "Record short demo video.", |
| "Publish social post.", |
| "Submit Space link, demo video link, and social post link by June 15, 2026.", |
| ] |
|
|
|
|
| def _http_status(url: str, *, timeout: int = 30) -> dict[str, Any]: |
| request = Request(url, headers={"User-Agent": "DOTA2Tuned submission checker"}) |
| try: |
| with urlopen(request, timeout=timeout) as response: |
| return {"ok": 200 <= response.status < 400, "status": response.status} |
| except HTTPError as exc: |
| return {"ok": False, "status": exc.code, "error": str(exc)} |
| except URLError as exc: |
| return {"ok": False, "status": None, "error": str(exc.reason)} |
|
|
|
|
| def _git_status() -> dict[str, Any]: |
| result = subprocess.run( |
| ["git", "status", "--short", "--branch"], |
| check=False, |
| capture_output=True, |
| text=True, |
| ) |
| lines = result.stdout.strip().splitlines() |
| changed = [line for line in lines[1:] if line.strip()] |
| return { |
| "ok": result.returncode == 0 and not changed, |
| "returncode": result.returncode, |
| "status": lines, |
| } |
|
|
|
|
| def _repo_has_file(api: HfApi, repo_id: str, repo_type: str, filename: str) -> dict[str, Any]: |
| try: |
| if repo_type == "model": |
| info = api.model_info(repo_id) |
| elif repo_type == "dataset": |
| info = api.dataset_info(repo_id) |
| else: |
| raise ValueError(f"unsupported repo_type: {repo_type}") |
| files = {sibling.rfilename for sibling in info.siblings} |
| return {"ok": filename in files, "file": filename, "repo": repo_id} |
| except Exception as exc: |
| return {"ok": False, "file": filename, "repo": repo_id, "error": repr(exc)} |
|
|
|
|
| def _space_runtime(api: HfApi, space_id: str) -> dict[str, Any]: |
| try: |
| info = api.space_info(space_id) |
| runtime = getattr(info, "runtime", None) |
| stage = getattr(runtime, "stage", str(runtime)) |
| return {"ok": stage == "RUNNING", "stage": stage, "space": space_id} |
| except Exception as exc: |
| return {"ok": False, "space": space_id, "error": repr(exc)} |
|
|
|
|
| def run_submission_checks(args: argparse.Namespace) -> dict[str, Any]: |
| token = os.getenv("HF_TOKEN") |
| api = HfApi(token=token) |
| space_id = args.space_repo |
| checks: dict[str, Any] = { |
| "git_clean": _git_status(), |
| "space_http": _http_status(args.space_url), |
| "modal_http": _http_status(args.modal_url), |
| "github_http": _http_status(args.github_repo), |
| "space_runtime": _space_runtime(api, space_id), |
| "model_card": _repo_has_file(api, args.model_repo, "model", "README.md"), |
| "dataset_card": _repo_has_file(api, args.dataset_repo, "dataset", "README.md"), |
| } |
| if not args.skip_public_api: |
| public_api = run_checks(args.space_url) |
| checks["public_space_api"] = { |
| "ok": all(item["status"] == "ok" for item in public_api["checks"]), |
| "checks": public_api["checks"], |
| } |
| return { |
| "generated_at": datetime.now(UTC).isoformat(), |
| "machine_checks": checks, |
| "manual_remaining": MANUAL_ITEMS, |
| "links": { |
| "space": args.space_url, |
| "github": args.github_repo, |
| "model": f"https://huggingface.co/{args.model_repo}", |
| "dataset": f"https://huggingface.co/datasets/{args.dataset_repo}", |
| "modal": args.modal_url, |
| }, |
| } |
|
|
|
|
| def _machine_ok(payload: dict[str, Any]) -> bool: |
| return all(check.get("ok") is True for check in payload["machine_checks"].values()) |
|
|
|
|
| def main() -> None: |
| parser = argparse.ArgumentParser(description="Run final DOTA2Tuned submission checks.") |
| parser.add_argument("--space-url", default=DEFAULT_SPACE) |
| parser.add_argument("--space-repo", default="build-small-hackathon/dota2tuned") |
| parser.add_argument("--model-repo", default=DEFAULT_MODEL_REPO) |
| parser.add_argument("--dataset-repo", default=DEFAULT_DATASET_REPO) |
| parser.add_argument("--modal-url", default=DEFAULT_MODAL_URL) |
| parser.add_argument("--github-repo", default=DEFAULT_GITHUB_REPO) |
| parser.add_argument( |
| "--output", |
| default="submission_evidence/final_submission_check.json", |
| help="Path for JSON evidence output.", |
| ) |
| parser.add_argument( |
| "--skip-public-api", |
| action="store_true", |
| help="Skip Gradio endpoint checks when only public artifact status is needed.", |
| ) |
| args = parser.parse_args() |
|
|
| payload = run_submission_checks(args) |
| output_path = Path(args.output) |
| output_path.parent.mkdir(parents=True, exist_ok=True) |
| output_path.write_text(json.dumps(payload, indent=2), encoding="utf-8") |
|
|
| print("Machine checks:") |
| for name, check in payload["machine_checks"].items(): |
| detail = check.get("stage") or check.get("status") or check.get("file") or "" |
| print(f"- {name}: {'ok' if check.get('ok') else 'fail'} {detail}") |
| print("\nManual remaining:") |
| for item in payload["manual_remaining"]: |
| print(f"- {item}") |
| print(f"\nWrote {output_path}") |
|
|
| if not _machine_ok(payload): |
| sys.exit(1) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|