fix(kattis,usaco): precision, open URLs, and Kattis submit error surface (#335)

## Problem

Kattis and USACO problem tests never extracted float precision, so
epsilon problems got no tolerance. Kattis `scrape_contest_metadata`
omitted `contest_url` and `standings_url`, breaking `:CP open
contest/standings`. Kattis submit always returned success even when the
server responded with an error (e.g. "You need to join the contest").

## Solution

Call `extract_precision` on problem HTML in both scrapers and emit it in
the JSON payload. Set `contest_url` and `standings_url` on Kattis
metadata paths. After Kattis submit, check for `Submission ID:` in the
response and surface the error text if absent.
This commit is contained in:
Barrett Ruth 2026-03-06 15:23:55 -05:00 committed by GitHub
parent c4be8b4f9e
commit 82640709d6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 20 additions and 4 deletions

View file

@ -10,7 +10,7 @@ from pathlib import Path
import httpx
from .base import BaseScraper
from .base import BaseScraper, extract_precision
from .timeouts import HTTP_TIMEOUT
from .models import (
ContestListResult,
@ -173,6 +173,7 @@ async def _stream_single_problem(client: httpx.AsyncClient, slug: str) -> None:
timeout_ms, memory_mb = _parse_limits(html)
interactive = _is_interactive(html)
precision = extract_precision(html)
tests: list[TestCase] = []
try:
@ -200,6 +201,7 @@ async def _stream_single_problem(client: httpx.AsyncClient, slug: str) -> None:
"memory_mb": memory_mb,
"interactive": interactive,
"multi_test": False,
"precision": precision,
}
),
flush=True,
@ -254,6 +256,8 @@ class KattisScraper(BaseScraper):
ProblemSummary(id=slug, name=name) for slug, name in slugs
],
url=f"{BASE_URL}/problems/%s",
contest_url=f"{BASE_URL}/contests/{contest_id}",
standings_url=f"{BASE_URL}/contests/{contest_id}/standings",
)
try:
html = await _fetch_text(
@ -273,6 +277,8 @@ class KattisScraper(BaseScraper):
contest_id=contest_id,
problems=[ProblemSummary(id=contest_id, name=name)],
url=f"{BASE_URL}/problems/%s",
contest_url=f"{BASE_URL}/problems/{contest_id}",
standings_url="",
)
except Exception as e:
return self._metadata_error(str(e))
@ -373,9 +379,15 @@ class KattisScraper(BaseScraper):
return self._submit_error(f"Submit request failed: {e}")
sid_m = re.search(r"Submission ID:\s*(\d+)", r.text, re.IGNORECASE)
sid = sid_m.group(1) if sid_m else ""
if not sid_m:
return self._submit_error(
r.text.strip() or "Submit failed (no submission ID)"
)
return SubmitResult(
success=True, error="", submission_id=sid, verdict="submitted"
success=True,
error="",
submission_id=sid_m.group(1),
verdict="submitted",
)
async def login(self, credentials: dict[str, str]) -> LoginResult: