Spaces:
Sleeping
Sleeping
| import json | |
| import os | |
| from datetime import datetime, timezone | |
| import gradio as gr | |
| import pandas as pd | |
| from auth_utils import get_user_greeting, get_daily_cap_info | |
| from hf_utils import ( | |
| _load_leaderboard_df, | |
| _load_user_submissions, | |
| _create_submission_record, | |
| _count_submissions_today, | |
| _get_cap_for_phase, | |
| _today_utc_str, | |
| ) | |
| from config import DAILY_SUBMISSION_CAP | |
| from challenges.object_localization.config import ( | |
| PHASES, CHALLENGE_TYPES, LEADERBOARD_METRICS, DEFAULT_SORT_METRIC, | |
| LEADERBOARD_FILE, CHALLENGE_PHASE, EVAL_DETAILS_MD, FORMAT_MD, | |
| ) | |
| from challenges.object_localization.validate import validate_submission | |
| def load_leaderboard(): | |
| try: | |
| df = _load_leaderboard_df(LEADERBOARD_FILE, LEADERBOARD_METRICS) | |
| except Exception as e: | |
| return pd.DataFrame(), f"β Could not load leaderboard: {e}" | |
| if not df.empty and "phase_codename" in df.columns: | |
| df = df[df["phase_codename"] == CHALLENGE_PHASE] | |
| if df.empty: | |
| return pd.DataFrame(), "βΉοΈ No scored Challenge phase submissions yet. Be the first!" | |
| df = df.sort_values(by=DEFAULT_SORT_METRIC, ascending=False, kind="mergesort") | |
| df_display = df.copy() | |
| df_display.insert(0, "Rank", range(1, len(df_display) + 1)) | |
| if "timestamp" in df_display.columns: | |
| df_display["Scored At"] = pd.to_datetime( | |
| df_display["timestamp"], unit="s", errors="coerce" | |
| ).dt.strftime("%d %b %Y, %I:%M %p") | |
| df_display.drop(columns=["timestamp"], inplace=True) | |
| for col in ["username", "email", "phase_codename", "submission_id"]: | |
| if col in df_display.columns: | |
| df_display.drop(columns=[col], inplace=True) | |
| return df_display, "" | |
| def handle_submit(file, team, model_name, phase_label, challenge_type, profile: gr.OAuthProfile | None): | |
| if profile is None: | |
| return "β You must be logged in with your HuggingFace account to submit.", "" | |
| username = profile.username | |
| email = getattr(profile, "email", "") or "" | |
| from config import SUBMISSIONS_TOKEN | |
| if not SUBMISSIONS_TOKEN: | |
| return "β Missing SUBMISSIONS_TOKEN. Add it in Space Settings β Secrets.", "" | |
| if file is None: | |
| return "β Please upload a JSON file.", "" | |
| if not team.strip(): | |
| return "β Please enter a Team / Display Name.", "" | |
| if not model_name.strip(): | |
| return "β Please enter a Model Name.", "" | |
| phase_codename = next((p["codename"] for p in PHASES if p["label"] == phase_label), phase_label) | |
| cap = _get_cap_for_phase(phase_codename) | |
| subs_today = _count_submissions_today(username, phase_codename) | |
| if subs_today >= cap: | |
| phase_label_str = "challenge" if phase_codename == "test-challenge2024" else "this" | |
| return f"β You've reached your daily limit of {cap} submission(s) for the {phase_label_str} phase. Come back tomorrow!", "" | |
| try: | |
| with open(file, "r", encoding="utf-8") as f: | |
| pred_obj = json.load(f) | |
| except Exception: | |
| return "β Could not parse JSON file.", "" | |
| ok, msg = validate_submission(pred_obj) | |
| if not ok: | |
| return f"β Invalid submission format: {msg}", "" | |
| original_filename = os.path.basename(file) | |
| try: | |
| submission_id = _create_submission_record( | |
| pred=pred_obj, team=team, model_name=model_name, | |
| phase_codename=phase_codename, challenge_type=challenge_type, | |
| original_filename=original_filename, username=username, email=email, | |
| ) | |
| except Exception as e: | |
| return f"β Upload failed: {e}", "" | |
| remaining = cap - subs_today - 1 | |
| return ( | |
| f"β Submission queued successfully! Visit **My Submissions** to see the results. " | |
| f"You have {remaining}/{cap} submissions remaining today for this phase.", | |
| submission_id, | |
| ) | |
| def load_my_submissions(phase_filter: str, profile: gr.OAuthProfile | None): | |
| if profile is None: | |
| return pd.DataFrame(), "β Please log in to view your submissions.", "" | |
| username = profile.username | |
| submissions = _load_user_submissions(username) | |
| if not submissions: | |
| return pd.DataFrame(), "βΉοΈ No submissions yet. Head to Submit Predictions to get started!", "" | |
| all_submissions = submissions[:] | |
| if phase_filter and phase_filter != "All": | |
| submissions = [s for s in submissions if s["phase"] == phase_filter] | |
| if not submissions: | |
| return pd.DataFrame(), f"βΉοΈ No submissions found for phase **{phase_filter}**.", "" | |
| state_icons = {"queued": "π‘", "running": "π΅", "done": "π’", "failed": "π΄", "unknown": "βͺ"} | |
| df = pd.DataFrame(submissions) | |
| if "timestamp" in df.columns: | |
| df["Submitted At"] = pd.to_datetime( | |
| df["timestamp"], unit="s", errors="coerce" | |
| ).dt.strftime("%d %b %Y, %I:%M %p") | |
| if "state" in df.columns: | |
| df["Status"] = df["state"].apply(lambda s: f"{state_icons.get(s, 'βͺ')} {s.capitalize()}") | |
| display_cols = ["Submitted At", "Status", "team", "model", "phase", "challenge_type", "error"] | |
| metric_cols = [m for m in LEADERBOARD_METRICS if m in df.columns] | |
| display_cols += metric_cols | |
| df_display = df[[c for c in display_cols if c in df.columns]].copy() | |
| df_display.rename(columns={ | |
| "team": "Team", "model": "Model", "phase": "Phase", | |
| "challenge_type": "Challenge Type", "error": "Error", | |
| }, inplace=True) | |
| for m in metric_cols: | |
| if m in df_display.columns: | |
| df_display[m] = df_display[m].apply(lambda x: f"{x:.4f}" if pd.notna(x) else "") | |
| total = len(all_submissions) | |
| done = sum(1 for s in all_submissions if s["state"] == "done") | |
| today_count = sum( | |
| 1 for s in all_submissions | |
| if datetime.fromtimestamp(s["timestamp"], tz=timezone.utc).strftime("%Y-%m-%d") == _today_utc_str() | |
| ) | |
| stats = ( | |
| f"**Total:** {total} | " | |
| f"**Scored:** {done} | " | |
| f"**Today:** {today_count}/{DAILY_SUBMISSION_CAP}" | |
| ) | |
| return df_display, "", stats | |
| def build_ol_page(demo: gr.Blocks) -> None: | |
| with demo.route("Object Localization", "/object-localization") as obj_loc: | |
| with gr.Row(): | |
| with gr.Column(scale=5): | |
| gr.Markdown("# π― Object Localization Challenge") | |
| gr.Markdown( | |
| "Submit bounding box and instance segmentation predictions " | |
| "evaluated automatically against hidden ground-truth annotations." | |
| ) | |
| with gr.Column(scale=1, min_width=160): | |
| gr.LoginButton(size="lg") | |
| ol_greeting = gr.Markdown("π Log in to submit.") | |
| gr.Markdown("---") | |
| with gr.Tabs(): | |
| # ββ Submit ββ | |
| with gr.TabItem("π Submit Predictions"): | |
| with gr.Row(): | |
| ol_submit_greeting = gr.Markdown("π Log in with HuggingFace to submit.") | |
| ol_cap_info = gr.Markdown("") | |
| gr.Markdown("---") | |
| with gr.Row(): | |
| with gr.Column(scale=3): | |
| gr.Markdown("#### Upload Submission File") | |
| ol_file_input = gr.File(label="Choose a JSON file", file_types=[".json"]) | |
| with gr.Accordion("π Submission Format", open=False): | |
| gr.Markdown(FORMAT_MD) | |
| with gr.Column(scale=2): | |
| gr.Markdown("#### Submission Info") | |
| ol_team_input = gr.Textbox(label="Team / Display Name", placeholder="e.g. My Awesome Team") | |
| ol_model_input = gr.Textbox(label="Model Name", placeholder="e.g. ResNet50-FPN") | |
| ol_phase_input = gr.Dropdown( | |
| label="Phase", | |
| choices=[p["label"] for p in PHASES], | |
| value=PHASES[0]["label"], | |
| ) | |
| ol_challenge_input = gr.Radio( | |
| label="Challenge Type", | |
| choices=CHALLENGE_TYPES, | |
| value=CHALLENGE_TYPES[0], | |
| ) | |
| ol_submit_btn = gr.Button("Submit (Queue for Evaluation)", variant="primary", size="lg") | |
| ol_submit_status = gr.Markdown("") | |
| ol_sid_box = gr.Code(label="Submission ID", language=None, visible=False) | |
| def do_ol_submit(file, team, model_name, phase_label, challenge_type, profile: gr.OAuthProfile | None): | |
| msg, sid = handle_submit(file, team, model_name, phase_label, challenge_type, profile) | |
| return msg, gr.update(value=sid, visible=bool(sid)) | |
| ol_submit_btn.click( | |
| do_ol_submit, | |
| inputs=[ol_file_input, ol_team_input, ol_model_input, ol_phase_input, ol_challenge_input], | |
| outputs=[ol_submit_status, ol_sid_box], | |
| ) | |
| def update_ol_submit_ui(profile: gr.OAuthProfile | None): | |
| return get_user_greeting(profile), get_daily_cap_info(profile, PHASES) | |
| obj_loc.load(update_ol_submit_ui, outputs=[ol_submit_greeting, ol_cap_info]) | |
| # ββ My Submissions ββ | |
| with gr.TabItem("π My Submissions"): | |
| ol_my_sub_greeting = gr.Markdown("π Log in with HuggingFace to view your submissions.") | |
| ol_my_sub_stats = gr.Markdown("") | |
| with gr.Row(): | |
| ol_phase_filter = gr.Dropdown( | |
| label="Filter by Phase", | |
| choices=["All"] + [p["codename"] for p in PHASES], | |
| value="All", | |
| scale=2, | |
| ) | |
| ol_refresh_my_btn = gr.Button("π Refresh", variant="secondary", scale=1) | |
| ol_my_sub_msg = gr.Markdown("") | |
| ol_my_sub_table = gr.Dataframe(interactive=False, wrap=True) | |
| def refresh_ol_my_subs(phase_filter, profile: gr.OAuthProfile | None): | |
| df, msg, stats = load_my_submissions(phase_filter, profile) | |
| return df, msg, stats, get_user_greeting(profile) | |
| ol_refresh_my_btn.click( | |
| refresh_ol_my_subs, | |
| inputs=[ol_phase_filter], | |
| outputs=[ol_my_sub_table, ol_my_sub_msg, ol_my_sub_stats, ol_my_sub_greeting], | |
| ) | |
| ol_phase_filter.change( | |
| refresh_ol_my_subs, | |
| inputs=[ol_phase_filter], | |
| outputs=[ol_my_sub_table, ol_my_sub_msg, ol_my_sub_stats, ol_my_sub_greeting], | |
| ) | |
| obj_loc.load( | |
| refresh_ol_my_subs, | |
| inputs=[ol_phase_filter], | |
| outputs=[ol_my_sub_table, ol_my_sub_msg, ol_my_sub_stats, ol_my_sub_greeting], | |
| ) | |
| # ββ Leaderboard ββ | |
| with gr.TabItem("π Leaderboard"): | |
| gr.Markdown("### Challenge Phase Rankings") | |
| gr.Markdown(f"Ranked by **{DEFAULT_SORT_METRIC}** (descending). Challenge phase only.") | |
| with gr.Accordion("π How is the Score Calculated?", open=False): | |
| gr.Markdown(EVAL_DETAILS_MD) | |
| ol_lb_msg = gr.Markdown("") | |
| ol_lb_table = gr.Dataframe(interactive=False, wrap=True) | |
| ol_refresh_lb_btn = gr.Button("π Refresh Leaderboard", variant="secondary", size="sm") | |
| def refresh_ol_leaderboard(profile: gr.OAuthProfile | None): | |
| df, msg = load_leaderboard() | |
| return df, msg, get_user_greeting(profile) | |
| ol_refresh_lb_btn.click(refresh_ol_leaderboard, outputs=[ol_lb_table, ol_lb_msg, ol_greeting]) | |
| obj_loc.load(refresh_ol_leaderboard, outputs=[ol_lb_table, ol_lb_msg, ol_greeting]) | |