Download GeoText-1652/scripts/render_leaderboard.py from geobase/GeoText1652_model: direct link, hf CLI and curl.
- Browser
- Download file 9.15 kB
-
https://huggingface.co/geobase/GeoText1652_model/resolve/main/GeoText-1652/scripts/render_leaderboard.py
- Command line
-
hf download hf://geobase/GeoText1652_model/GeoText-1652/scripts/render_leaderboard.py
-
curl -L -o render_leaderboard.py https://huggingface.co/geobase/GeoText1652_model/resolve/main/GeoText-1652/scripts/render_leaderboard.py
9.15 kB
| #!/usr/bin/env python3 | |
| """Render the GeoText-1652 leaderboard from structured JSON data.""" | |
| from __future__ import annotations | |
| import argparse | |
| import json | |
| import sys | |
| from pathlib import Path | |
| from typing import Any | |
| ROOT = Path(__file__).resolve().parents[1] | |
| DATA_PATH = ROOT / "leaderboard" / "data.json" | |
| OUTPUT_PATH = ROOT / "LEADERBOARD.md" | |
| REQUIRED_ENTRY_FIELDS = { | |
| "id", | |
| "method", | |
| "submitter", | |
| "training_data", | |
| "backbone", | |
| "metrics", | |
| } | |
| REQUIRED_METRICS = ("txt_r1", "txt_r5", "txt_r10", "img_r1", "img_r5", "img_r10") | |
| def fail(message: str) -> None: | |
| raise SystemExit(f"leaderboard: {message}") | |
| def load_data(path: Path) -> dict[str, Any]: | |
| try: | |
| with path.open("r", encoding="utf-8") as file: | |
| data = json.load(file) | |
| except FileNotFoundError: | |
| fail(f"missing data file: {path}") | |
| except json.JSONDecodeError as exc: | |
| fail(f"invalid JSON in {path}: {exc}") | |
| if not isinstance(data, dict): | |
| fail("top-level data must be a JSON object") | |
| return data | |
| def as_float(value: Any, field: str, entry_id: str) -> float: | |
| if isinstance(value, bool) or not isinstance(value, (int, float)): | |
| fail(f"{entry_id}: metric {field} must be a number") | |
| value = float(value) | |
| if value < 0.0 or value > 100.0: | |
| fail(f"{entry_id}: metric {field} must be between 0 and 100") | |
| return value | |
| def enrich_entry(entry: dict[str, Any]) -> dict[str, Any]: | |
| entry_id = str(entry.get("id", "<missing-id>")) | |
| missing = sorted(REQUIRED_ENTRY_FIELDS - set(entry)) | |
| if missing: | |
| fail(f"{entry_id}: missing required fields: {', '.join(missing)}") | |
| metrics = entry["metrics"] | |
| if not isinstance(metrics, dict): | |
| fail(f"{entry_id}: metrics must be an object") | |
| for metric in REQUIRED_METRICS: | |
| if metric not in metrics: | |
| fail(f"{entry_id}: missing required metric {metric}") | |
| metrics[metric] = as_float(metrics[metric], metric, entry_id) | |
| txt_mean = sum(metrics[name] for name in ("txt_r1", "txt_r5", "txt_r10")) / 3.0 | |
| img_mean = sum(metrics[name] for name in ("img_r1", "img_r5", "img_r10")) / 3.0 | |
| r_mean = (txt_mean + img_mean) / 2.0 | |
| for key, value in { | |
| "txt_r_mean": txt_mean, | |
| "img_r_mean": img_mean, | |
| "r_mean": r_mean, | |
| }.items(): | |
| if key in metrics: | |
| supplied = as_float(metrics[key], key, entry_id) | |
| if abs(supplied - value) > 0.05: | |
| fail(f"{entry_id}: supplied {key}={supplied:.3f} does not match computed {value:.3f}") | |
| metrics[key] = value | |
| return entry | |
| def validate(data: dict[str, Any]) -> None: | |
| if data.get("primary_metric") != "r_mean": | |
| fail("primary_metric must be r_mean") | |
| leaderboards = data.get("leaderboards") | |
| if not isinstance(leaderboards, list) or not leaderboards: | |
| fail("leaderboards must be a non-empty list") | |
| seen_splits: set[str] = set() | |
| seen_entries: set[str] = set() | |
| for board in leaderboards: | |
| if not isinstance(board, dict): | |
| fail("each leaderboard must be an object") | |
| split = board.get("split") | |
| name = board.get("name") | |
| entries = board.get("entries") | |
| if not split or not isinstance(split, str): | |
| fail("each leaderboard requires a string split") | |
| if split in seen_splits: | |
| fail(f"duplicate leaderboard split: {split}") | |
| seen_splits.add(split) | |
| if not name or not isinstance(name, str): | |
| fail(f"{split}: requires a string name") | |
| if not isinstance(entries, list): | |
| fail(f"{split}: entries must be a list") | |
| for entry in entries: | |
| if not isinstance(entry, dict): | |
| fail(f"{split}: each entry must be an object") | |
| entry_id = entry.get("id") | |
| if not entry_id or not isinstance(entry_id, str): | |
| fail(f"{split}: each entry requires a string id") | |
| scoped_id = f"{split}:{entry_id}" | |
| if scoped_id in seen_entries: | |
| fail(f"duplicate entry id in {split}: {entry_id}") | |
| seen_entries.add(scoped_id) | |
| enrich_entry(entry) | |
| def escape_cell(value: Any) -> str: | |
| if value is None: | |
| return "-" | |
| text = str(value).strip() | |
| if not text: | |
| return "-" | |
| return text.replace("|", "\\|").replace("\n", " ") | |
| def link_or_text(label: str, url: Any) -> str: | |
| if not isinstance(url, str) or not url.strip(): | |
| return "-" | |
| url = url.strip() | |
| return f"[{label}]({url})" | |
| def fmt_metric(value: float) -> str: | |
| return f"{value + 1e-9:.1f}" | |
| def entry_sort_key(entry: dict[str, Any]) -> tuple[float, float, float, str]: | |
| metrics = entry["metrics"] | |
| return ( | |
| metrics["r_mean"], | |
| metrics["txt_r1"], | |
| metrics["img_r1"], | |
| entry["method"].lower(), | |
| ) | |
| def render_board(board: dict[str, Any]) -> list[str]: | |
| entries = sorted(board["entries"], key=entry_sort_key, reverse=True) | |
| lines = [ | |
| f"## {board['name']}", | |
| "", | |
| str(board.get("description", "")).strip(), | |
| "", | |
| ] | |
| if not entries: | |
| lines.extend([ | |
| "No public submissions yet. Submit a PR by adding an entry to `leaderboard/data.json`.", | |
| "", | |
| ]) | |
| return lines | |
| header = [ | |
| "Rank", | |
| "Method", | |
| "Submitter", | |
| "Training Data", | |
| "Backbone", | |
| "Text R@1", | |
| "Text R@5", | |
| "Text R@10", | |
| "Image R@1", | |
| "Image R@5", | |
| "Image R@10", | |
| "Mean", | |
| "Links", | |
| "Verified", | |
| ] | |
| lines.append("| " + " | ".join(header) + " |") | |
| lines.append("| " + " | ".join(["---"] * len(header)) + " |") | |
| for rank, entry in enumerate(entries, start=1): | |
| metrics = entry["metrics"] | |
| links = " ".join( | |
| part | |
| for part in ( | |
| link_or_text("paper", entry.get("paper")), | |
| link_or_text("code", entry.get("code")), | |
| link_or_text("ckpt", entry.get("checkpoint")), | |
| ) | |
| if part != "-" | |
| ) or "-" | |
| row = [ | |
| str(rank), | |
| escape_cell(entry["method"]), | |
| escape_cell(entry["submitter"]), | |
| escape_cell(entry["training_data"]), | |
| escape_cell(entry["backbone"]), | |
| fmt_metric(metrics["txt_r1"]), | |
| fmt_metric(metrics["txt_r5"]), | |
| fmt_metric(metrics["txt_r10"]), | |
| fmt_metric(metrics["img_r1"]), | |
| fmt_metric(metrics["img_r5"]), | |
| fmt_metric(metrics["img_r10"]), | |
| f"**{fmt_metric(metrics['r_mean'])}**", | |
| links, | |
| "yes" if entry.get("verified") else "pending", | |
| ] | |
| lines.append("| " + " | ".join(row) + " |") | |
| lines.append("") | |
| return lines | |
| def render(data: dict[str, Any]) -> str: | |
| lines = [ | |
| "# GeoText-1652 Leaderboard", | |
| "", | |
| "<!-- This file is generated by scripts/render_leaderboard.py. Edit leaderboard/data.json instead. -->", | |
| "", | |
| "The primary ranking metric is **Mean**, computed as the average of Text R@1/R@5/R@10 and Image R@1/R@5/R@10 means. All scores are percentages.", | |
| "", | |
| "Use the official evaluation command from the README and report the metrics printed by `Method/re_bbox.py`.", | |
| "", | |
| ] | |
| for board in data["leaderboards"]: | |
| lines.extend(render_board(board)) | |
| lines.extend( | |
| [ | |
| "## Submit Results", | |
| "", | |
| "Open a pull request that edits `leaderboard/data.json`, then run:", | |
| "", | |
| "```bash", | |
| "python3 scripts/render_leaderboard.py", | |
| "```", | |
| "", | |
| "Required fields for each entry:", | |
| "", | |
| "- `id`: stable lowercase identifier, unique within the split", | |
| "- `method`: model or system name", | |
| "- `submitter`: person, team, or organization", | |
| "- `training_data`: training data used, including any external data", | |
| "- `backbone`: visual/text backbone or main model family", | |
| "- `metrics`: `txt_r1`, `txt_r5`, `txt_r10`, `img_r1`, `img_r5`, `img_r10`", | |
| "", | |
| "Please include paper/code/checkpoint links when public. Mark `verified` as `false` unless the maintainers have reproduced or audited the result.", | |
| "", | |
| ] | |
| ) | |
| return "\n".join(lines) | |
| def main() -> int: | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--check", action="store_true", help="fail if LEADERBOARD.md is out of date") | |
| args = parser.parse_args() | |
| data = load_data(DATA_PATH) | |
| validate(data) | |
| rendered = render(data) | |
| if args.check: | |
| current = OUTPUT_PATH.read_text(encoding="utf-8") if OUTPUT_PATH.exists() else "" | |
| if current != rendered: | |
| print("LEADERBOARD.md is out of date. Run: python3 scripts/render_leaderboard.py", file=sys.stderr) | |
| return 1 | |
| return 0 | |
| OUTPUT_PATH.write_text(rendered, encoding="utf-8") | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) | |