Skip to content

fix(scripts): classify DMOJ problems and backfill degraded metadata - #141

Merged
cameroncuster merged 2 commits into
mainfrom
fix/dmoj-classifier-backfill
Aug 26, 2026
Merged

fix(scripts): classify DMOJ problems and backfill degraded metadata#141
cameroncuster merged 2 commits into
mainfrom
fix/dmoj-classifier-backfill

Conversation

@cameroncuster

Copy link
Copy Markdown
Owner

Why

Two problems surfaced after #140 merged.

DMOJ is unreachable from any server we control. Probed from three network positions:

Origin Result
Maintainer machine (residential), curl + node, 4 UA variants 200
Vercel production /api/dmoj, 5 consecutive 403
GitHub Actions runner (Azure 52.225.25.54), 2 UA variants 403

Cloudflare rejects datacenter egress and User-Agent makes no difference, so neither a header tweak nor a scheduled CI job fixes it. DMOJ also sends no access-control-allow-origin, ruling out a browser-side fetch. A submitted DMOJ problem therefore lands in the degraded state the ingestion fallback produces: name equals the bare problem code, no tags. ciw26p2 is live in that state today.

The classifier never learned about DMOJ. get_problem_source was still two-way, so a DMOJ URL fell into the Codeforces branch, failed to parse, and the row never got a type. That is a downstream miss from #140.

What changed

scripts/classify_problems.py gains DMOJ alongside the existing providers:

  • get_problem_source returns "dmoj" for dmoj.ca URLs
  • extract_dmoj_info parses dmoj.ca/problem/<code>, rejecting bare codes for the same reason the web ingestion does
  • fetch_dmoj_problem reads name and types from api/v2/problem/<code>; the API carries no statement, so classification uses name and tags alone
  • is_degraded_metadata plus a write in the main loop: when a row's name still equals its DMOJ problem code, name and tags are refreshed from the API

The backfill is deliberately narrow. It fires only when the stored name is exactly the problem code, so curated Kattis and Codeforces names cannot be clobbered, and a DMOJ row that already has a real name is left alone.

No new dependencies — requests and psycopg were already in use.

Running it

From a machine with residential egress, after applying:

python scripts/classify_problems.py --all

--all is required to reach rows that already have a type set; the default query only selects WHERE type IS NULL.

Validation

  • python -m compileall -q scripts, 11 unittests pass (4 new DMOJ fetcher tests, 2 new degraded-metadata tests)
  • python scripts/classify_problems.py --help
  • eslint . and prettier --check . clean (Python is outside both)
  • Live check against the real API from a residential IP:
    extract: {'problemId': 'ciw26p2', 'url': 'https://dmoj.ca/problem/ciw26p2'}
    fetch:   {'name': "CIW '26 P2 - Number Shuffle", 'tags': ['Simulation'], 'statement': ''}
    degraded before backfill: True
    

One incidental diff

The editor stripped trailing whitespace from blank lines inside the Gemini prompt f-string. Whitespace-only, no semantic change to the prompt, and I could not reintroduce it through the editing tool.

The classifier's source detection was still two-way, so a DMOJ URL fell
into the Codeforces branch, failed to parse, and the row never got a
type. Add DMOJ detection, URL extraction, and an API fetcher alongside
the existing providers.

DMOJ's Cloudflare rejects datacenter egress, so the /api/dmoj proxy
returns 403 from Vercel and a submitted problem is stored with its bare
code as the name and no tags. This script runs from a maintainer
machine, where the API answers, so it also repairs those rows: when a
row's name still equals its DMOJ problem code, name and tags are
refreshed from the API. Rows on other providers, and DMOJ rows with a
real name, are left untouched.
@vercel

vercel Bot commented Aug 26, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
gitgud Ready Ready Preview Aug 26, 2026 8:16pm

main() constructed the Gemini client before touching the database, so a
metadata repair required an API key it never uses. --backfill-only skips
the client, narrows the query to DMOJ rows, and skips the type write, so
the run only refreshes name and tags on rows still holding a bare
problem code.
@cameroncuster
cameroncuster merged commit 94cf0c1 into main Aug 26, 2026
14 checks passed
@cameroncuster
cameroncuster deleted the fix/dmoj-classifier-backfill branch August 26, 2026 21:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant