abidlabs's picture
abidlabs HF Staff
Add app.py
dfd58db verified
Raw
History Blame
5.99 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 (HTML -> PNG via the
shared renderer Space) with the participant's verified-claim stats.
"""
import inspect
import json
import os
import tempfile
import urllib.parse
import gradio as gr
from gradio_client import Client, handle_file
RENDERER_SPACE = "https://ysharma-hackathon-certificate-html-to-image.hf.space/"
DISCUSSIONS_URL = "https://huggingface.co/spaces/ICML-2026-agent-repro/challenge/discussions"
_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)
_client = None
def get_renderer():
global _client
if _client is None:
params = inspect.signature(Client.__init__).parameters
kwargs = {}
token = os.getenv("HF_TOKEN")
if token:
kwargs["hf_token" if "hf_token" in params else "token"] = token
_client = Client(RENDERER_SPACE, **kwargs)
return _client
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), 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), gr.update(visible=True),
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), gr.update(visible=False),
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))
with tempfile.NamedTemporaryFile("w", delete=False, suffix=".html", encoding="utf-8") as f:
f.write(html)
path = f.name
try:
image_path = get_renderer().predict(html_file=handle_file(path), api_name="/predict")[0]
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)
with gr.Column(visible=False) as contact_box:
pass
demo.load(on_load, inputs=None, outputs=[main_box, contact_box, status, name_box])
gen_btn.click(create_certificate, inputs=[name_box], outputs=[cert_image, note, linkedin])
demo.launch()