abidlabs's picture
abidlabs HF Staff
Fix renderer: in-Space chromium + partner logos (app.py)
7008665 verified
Raw
History Blame
6.39 kB
"""ICML 2026 Open Reproductions β€” Certificate Generator
=======================================================
1. Sign in with Hugging Face (OAuth).
2. Eligibility is checked against the frozen challenge verdicts: every
participant with at least one claim judged `verified` qualifies
(baked into eligible.json at challenge close β€” 267 participants).
3. The display name is editable; the certificate renders to PNG with headless
Chromium (installed via packages.txt) β€” no external renderer dependency.
"""
import json
import os
import shutil
import subprocess
import tempfile
import urllib.parse
import gradio as gr
DISCUSSIONS_URL = "https://huggingface.co/spaces/ICML-2026-agent-repro/challenge/discussions"
CANVAS = (2000, 1414) # must match the body size in certificate_template.html
_HERE = os.path.dirname(__file__)
with open(os.path.join(_HERE, "certificate_template.html"), encoding="utf-8") as f:
TEMPLATE = f.read()
with open(os.path.join(_HERE, "eligible.json"), encoding="utf-8") as f:
ELIGIBLE = json.load(f)
def find_chromium():
candidates = [
"chromium", "chromium-browser", "google-chrome", "google-chrome-stable",
"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome", # local dev
]
for c in candidates:
path = shutil.which(c) or (c if os.path.exists(c) else None)
if path:
return path
raise RuntimeError("No Chromium binary found β€” is `chromium` in packages.txt?")
def render_html(html: str) -> str:
out = os.path.join(tempfile.mkdtemp(), "certificate.png")
with tempfile.NamedTemporaryFile("w", delete=False, suffix=".html", encoding="utf-8") as f:
f.write(html)
src = f.name
try:
subprocess.run(
[find_chromium(), "--headless=new", "--no-sandbox", "--disable-gpu",
"--hide-scrollbars", "--force-device-scale-factor=1",
f"--window-size={CANVAS[0]},{CANVAS[1]}", f"--screenshot={out}",
f"file://{src}"],
check=True, capture_output=True, timeout=120,
)
finally:
try:
os.unlink(src)
except OSError:
pass
return out
def plural(n, word):
return f"{n} {word}" + ("" if n == 1 else "s")
def stats_line(rec):
return (f"{plural(rec['verified_claims'], 'claim')} verified across "
f"{plural(rec['verified_papers'], 'paper')}")
def on_load(profile: gr.OAuthProfile | None):
if profile is None:
return (gr.update(visible=False),
"### πŸ‘‹ Sign in with Hugging Face to check your eligibility.", "")
rec = ELIGIBLE.get(profile.username.lower())
if rec is None:
return (gr.update(visible=False),
f"### Sorry @{profile.username} β€” no verified logbook found.\n"
"Certificates go to participants with at least one claim judged "
"**verified** by the Logbook Judge before the Aug 2 deadline. "
f"If you believe this is an error, please open a thread in the "
f"[challenge discussions]({DISCUSSIONS_URL}).", "")
return (gr.update(visible=True),
f"### πŸŽ‰ Congratulations @{profile.username} β€” you qualify!\n"
f"Your record: **{stats_line(rec)}** ({plural(rec['logbooks'], 'logbook')} judged). "
"Adjust how your name should appear, then generate your certificate.",
profile.name or profile.username)
def linkedin_url(name):
p = {
"startTask": "CERTIFICATION_NAME",
"name": "ICML 2026 Open Reproductions β€” Certificate of Participation",
"organizationName": "Hugging Face",
"issueYear": "2026", "issueMonth": "8",
"certUrl": "https://huggingface.co/spaces/ICML-2026-agent-repro/certificate-generator",
}
return "https://www.linkedin.com/profile/add?" + urllib.parse.urlencode(p)
def create_certificate(display_name, profile: gr.OAuthProfile | None):
if profile is None:
return None, "❌ Please sign in first.", gr.update(visible=False)
rec = ELIGIBLE.get(profile.username.lower())
if rec is None:
return None, "❌ No verified logbook found for your account.", gr.update(visible=False)
name = (display_name or "").strip() or profile.name or profile.username
html = TEMPLATE.replace("{participant_name}", name).replace("{stats_line}", stats_line(rec))
try:
image_path = render_html(html)
except Exception as e:
return None, f"❌ Rendering failed: {e}. Please try again in a minute.", gr.update(visible=False)
btn = (f'<a href="{linkedin_url(name)}" target="_blank" '
f'style="display:inline-block;background:#0a66c2;color:#fff;font-weight:700;'
f'padding:10px 22px;border-radius:8px;text-decoration:none">Add to LinkedIn profile β†’</a>')
return image_path, "πŸŽ‰ Your certificate is ready β€” download it below.", gr.update(value=btn, visible=True)
THEME = gr.themes.Base(
primary_hue=gr.themes.colors.orange,
neutral_hue=gr.themes.colors.stone,
)
CSS = """
.gradio-container {
background-color: #fdfcf9 !important;
background-image:
linear-gradient(rgba(31,41,55,.045) 1px, transparent 1px),
linear-gradient(90deg, rgba(31,41,55,.045) 1px, transparent 1px);
background-size: 26px 26px;
}
#hero h1 { font-family: ui-serif, "Iowan Old Style", Georgia, serif; }
"""
with gr.Blocks(title="ICML 2026 Open Reproductions β€” Certificates", theme=THEME, css=CSS) as demo:
gr.Markdown("# πŸŽ“ ICML 2026 Open Reproductions β€” Certificate of Participation", elem_id="hero")
gr.Markdown(
"Every participant with **at least one claim judged `verified`** in the challenge "
"(July 15 – August 2, 2026) can generate their certificate here."
)
login = gr.LoginButton()
status = gr.Markdown()
with gr.Column(visible=False) as main_box:
name_box = gr.Textbox(label="Name on the certificate")
gen_btn = gr.Button("Generate my certificate πŸŽ“", variant="primary")
cert_image = gr.Image(label="Your certificate", type="filepath", interactive=False)
note = gr.Markdown()
linkedin = gr.HTML(visible=False)
demo.load(on_load, inputs=None, outputs=[main_box, status, name_box])
gen_btn.click(create_certificate, inputs=[name_box], outputs=[cert_image, note, linkedin])
demo.launch()