Spaces:
Running
Running
import json | |
from datetime import datetime, timezone | |
from pathlib import Path | |
import gradio as gr | |
from huggingface_hub import snapshot_download | |
from loguru import logger | |
from pydantic import BaseModel, Field | |
from envs import API, REPO_ID, USERS_PATH, USERS_REPO | |
from formatting import styled_error, styled_message, styled_warning | |
css = """ | |
:root { | |
color-scheme: light !important; | |
--block-border-width: 0 !important; | |
--section-header-text-weight: 600 !important; | |
--section-header-text-size: 14px !important; | |
--input-radius: var(--radius-xl) !important; | |
--font-mono: "Space Mono", monospace !important; | |
--text-sm: 12px !important; | |
--text-md: 14px !important; | |
--text-lg: 16px !important; | |
--input-text-size: var(--text-sm) !important; | |
--body-text-size: 14px !important; | |
--input-background-fill-focus: var(--secondary-300) !important; | |
// Button Colors | |
--button-primary-background-fill: var(--primary-800) !important; | |
--button-secondary-background-fill: var(--secondary-600) !important; | |
--checkbox-label-text-color: var(--body-text-color) !important; | |
--card-bg-color: #fcecd4; | |
--card-btn-color: #D4E4FC; | |
--card-btn-color-hover: #7DAEF6; | |
--answer-bg-color: #f0f8ff; | |
--hover-border-color: #121212; | |
} | |
:root .dark { | |
color-scheme: dark !important; | |
--block-border-width: 0 !important; | |
--section-header-text-weight: 600 !important; | |
--section-header-text-size: 14px !important; | |
--input-radius: var(--radius-xl) !important; | |
--font-mono: "Space Mono", monospace !important; | |
--text-sm: 12px !important; | |
--text-md: 14px !important; | |
--text-lg: 16px !important; | |
--input-text-size: var(--text-sm) !important; | |
--body-text-size: 14px !important; | |
--button-primary-background-fill: var(--neutral-100) !important; | |
--button-secondary-background-fill: var(--secondary-300) !important; | |
--button-primary-text-color: black !important; | |
--button-secondary-text-color: black !important; | |
--checkbox-label-text-color: var(--body-text-color) !important; | |
--card-bg-color: #383127; | |
--answer-bg-color: #1a2b3c; | |
--hover-border-color: #ffffff; | |
} | |
.gradio-app { | |
} | |
.warning-header { | |
color: red; | |
} | |
.neutral-header { | |
color: blue; | |
} | |
.success-header { | |
color: green; | |
} | |
.gradio-container { | |
max-width: 1000px; | |
margin: 0 auto; | |
padding: 0 8px; | |
} | |
""" | |
def restart_space(): | |
API.restart_space(repo_id=REPO_ID) | |
def download_dataset_snapshot(repo_id, local_dir): | |
try: | |
logger.info(f"Downloading dataset snapshot from {repo_id} to {local_dir}") | |
snapshot_download( | |
repo_id=repo_id, | |
local_dir=local_dir, | |
repo_type="dataset", | |
tqdm_class=None, | |
) | |
except Exception as e: | |
logger.error(f"Error downloading dataset snapshot from {repo_id} to {local_dir}: {e}. Restarting space.") | |
API.restart_space(repo_id=repo_id) | |
download_dataset_snapshot(USERS_REPO, USERS_PATH) | |
class User(BaseModel): | |
""" | |
Represents a user in the competition system, formatted for HuggingFace datasets. | |
""" | |
username: str = Field(description="HuggingFace username of the user") | |
name: str = Field(description="Full name of the user") | |
email: str = Field(description="Contact email of the user") | |
affiliation: str = Field(description="Affiliation of the user") | |
def create_user( | |
username: str, | |
name: str, | |
email: str, | |
affiliation: str, | |
profile: gr.OAuthProfile | None, | |
) -> User: | |
""" | |
Create a user for a tossup model. | |
Args: | |
name: Display name of the submission | |
description: Detailed description of what the submission does | |
user_email: Email of the user who created the submission | |
workflow: The workflow configuration for the tossup model | |
Returns: | |
Submission object if successful, None if validation fails | |
""" | |
# Create the submission | |
dt = datetime.now(timezone.utc) | |
submission = User( | |
username=username, | |
name=name, | |
email=email, | |
affiliation=affiliation, | |
created_at=dt.isoformat(), | |
) | |
return submission | |
def get_user(username: str) -> User | None: | |
""" | |
Get a user from the registered users dataset. | |
""" | |
out_path = Path(f"{USERS_PATH}/{username}.json") | |
if not out_path.exists(): | |
return None | |
with out_path.open("r") as f: | |
user_dict = json.load(f) | |
return User.model_validate(user_dict) | |
def is_user_logged_in(profile: gr.OAuthProfile | None) -> bool: | |
""" | |
Check if a user is logged in. | |
""" | |
return profile is not None | |
def user_signup( | |
name: str, | |
email: str, | |
affiliation: str, | |
profile: gr.OAuthProfile | None = None, | |
) -> User: | |
""" | |
Sign up for the competition. | |
""" | |
if profile is None: | |
return styled_error("Please sign in using your HuggingFace account to register for the competition.") | |
try: | |
username = profile.username | |
user = get_user(username) | |
new_user = create_user( | |
username=username, | |
name=name, | |
email=email, | |
affiliation=affiliation, | |
profile=profile, | |
) | |
# Convert to dictionary format | |
user_dict = new_user.model_dump() | |
# Upload to HuggingFace dataset | |
updated = bool(user) | |
API.upload_file( | |
path_or_fileobj=json.dumps(user_dict, indent=2).encode(), | |
path_in_repo=f"{username}.json", | |
repo_id=USERS_REPO, | |
repo_type="dataset", | |
commit_message=f"{'Update' if updated else 'Add'} user {username}", | |
) | |
return styled_message( | |
f"Successfully {'updated' if updated else 'registered'} user {username} for the competition!<br>" | |
) | |
except Exception as e: | |
logger.exception(e) | |
return styled_error( | |
"Error registering for the competition. Please try again later, or contact the organizers at <br><a href='mailto:[email protected]'>[email protected]</a>." | |
) | |
def load_user_info(profile: gr.OAuthProfile | None): | |
if profile is None: | |
return ( | |
gr.update(interactive=False), | |
gr.update(interactive=False), | |
gr.update(interactive=False), | |
gr.update(interactive=False), | |
gr.update( | |
value="<h2 class='warning-header'>Please sign in using your HuggingFace account to register for the competition.</h2>", | |
), | |
) | |
user = get_user(profile.username) | |
if user is None: | |
return ( | |
gr.update(interactive=True), | |
gr.update(interactive=True), | |
gr.update(interactive=True), | |
gr.update(interactive=True, value="Register"), | |
gr.update( | |
value="<h2 class='neutral-header'>Fill in your details below to register for the competition.</h2>", | |
), | |
) | |
return ( | |
gr.update(interactive=True, value=user.name), | |
gr.update(interactive=True, value=user.email), | |
gr.update(interactive=True, value=user.affiliation), | |
gr.update(interactive=True, value="Update Information"), | |
gr.update( | |
value="<h2 class='success-header'>You are already registered! You can update your details below.</h2>", | |
), | |
) | |
with gr.Blocks(title="Register - QANTA 2025 Quizbowl System Submission", css=css) as demo: | |
gr.Markdown("# Register for QANTA 2025 Quizbowl System Submission") | |
with gr.Row(): | |
with gr.Column(): | |
# HuggingFace Login Button | |
hf_login_btn = gr.LoginButton(scale=1) | |
header = gr.HTML("<h2>Fill in your details below</h2>") | |
# Registration form | |
name = gr.Textbox(label="Full Name", placeholder="Enter your full name") | |
email = gr.Textbox(label="Contact Email", placeholder="Enter your email") | |
affiliation = gr.Textbox( | |
label="Affiliation", | |
placeholder="Enter your organization or institution", | |
) | |
submit_btn = gr.Button("Register") | |
status = gr.Markdown() | |
submit_btn.click( | |
fn=user_signup, | |
inputs=[name, email, affiliation], | |
outputs=[status], | |
) | |
demo.load( | |
load_user_info, | |
inputs=[], | |
outputs=[name, email, affiliation, submit_btn, header], | |
) | |
demo.queue(default_concurrency_limit=40).launch() | |