Spaces:
Runtime error
Runtime error
| import gradio as gr | |
| import json | |
| from datetime import datetime, timezone | |
| from datasets import Dataset, load_dataset | |
| from huggingface_hub import login, HfApi | |
| import pandas as pd | |
| import os | |
| import time | |
| import tempfile | |
| import logging | |
| # Setup logging | |
| logging.basicConfig(level=logging.INFO) | |
| logger = logging.getLogger(__name__) | |
| # Authenticate with Hugging Face | |
| HF_TOKEN = os.environ.get("HF_TOKEN") | |
| if HF_TOKEN: | |
| login(token=HF_TOKEN) | |
| logger.info("β Authenticated with Hugging Face") | |
| else: | |
| logger.warning("β οΈ HF_TOKEN not found - running without authentication") | |
| # Replace with your actual dataset name for Winter 2025 | |
| DATASET_NAME = "ysharma/gradio-hackathon-registrations-winter-2025" | |
| def get_countdown_html(): | |
| """Generate countdown HTML with current time values calculated in Python""" | |
| # Target date: November 13, 2025, 11:59 PM UTC | |
| target_date = datetime(2025, 11, 13, 23, 59, 59, tzinfo=timezone.utc) | |
| now = datetime.now(timezone.utc) | |
| difference = target_date - now | |
| if difference.total_seconds() > 0: | |
| total_seconds = int(difference.total_seconds()) | |
| days = total_seconds // (24 * 3600) | |
| hours = (total_seconds % (24 * 3600)) // 3600 | |
| minutes = (total_seconds % 3600) // 60 | |
| seconds = total_seconds % 60 | |
| else: | |
| days = hours = minutes = seconds = 0 | |
| # Calculate progress for circles (stroke-dashoffset calculation) | |
| # The circumference is 283, so we calculate the offset | |
| days_offset = 283 - min((days / 365) * 283, 283) | |
| hours_offset = 283 - ((hours / 24) * 283) | |
| minutes_offset = 283 - ((minutes / 60) * 283) | |
| seconds_offset = 283 - ((seconds / 60) * 283) | |
| return f""" | |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Hackathon Countdown</title> | |
| <style> | |
| * {{ | |
| margin: 0; | |
| padding: 0; | |
| box-sizing: border-box; | |
| }} | |
| body {{ | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; | |
| margin: 0; | |
| padding: 20px; | |
| }} | |
| .countdown-container {{ | |
| background: #C8DDD7; | |
| border-radius: 20px; | |
| padding: 40px; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); | |
| text-align: center; | |
| max-width: 600px; | |
| width: 100%; | |
| margin: 0 auto; | |
| display: block; | |
| }} | |
| .title {{ | |
| font-size: 28px; | |
| font-weight: 600; | |
| color: #000000; | |
| margin-bottom: 40px; | |
| }} | |
| .countdown {{ | |
| display: flex; | |
| justify-content: space-around; | |
| align-items: center; | |
| gap: 20px; | |
| flex-wrap: wrap; | |
| }} | |
| .time-unit {{ | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| min-width: 100px; | |
| }} | |
| .circle {{ | |
| position: relative; | |
| width: 100px; | |
| height: 100px; | |
| margin-bottom: 15px; | |
| }} | |
| .circle svg {{ | |
| width: 100%; | |
| height: 100%; | |
| transform: rotate(-90deg); | |
| }} | |
| .circle-bg {{ | |
| fill: none; | |
| stroke: rgba(0, 0, 0, 0.15); | |
| stroke-width: 6; | |
| }} | |
| .circle-progress {{ | |
| fill: none; | |
| stroke: #000000; | |
| stroke-width: 6; | |
| stroke-linecap: round; | |
| stroke-dasharray: 283; | |
| transition: stroke-dashoffset 0.3s ease; | |
| }} | |
| .number {{ | |
| position: absolute; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| font-size: 32px; | |
| font-weight: 700; | |
| color: #000000; | |
| font-family: 'Courier New', monospace; | |
| }} | |
| .label {{ | |
| font-size: 16px; | |
| font-weight: 500; | |
| color: #000000; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| }} | |
| @keyframes pulse {{ | |
| 0% {{ opacity: 1; }} | |
| 50% {{ opacity: 0.8; }} | |
| 100% {{ opacity: 1; }} | |
| }} | |
| .number {{ | |
| animation: pulse 1s ease-in-out infinite; | |
| }} | |
| @media (max-width: 480px) {{ | |
| .countdown {{ | |
| gap: 15px; | |
| }} | |
| .circle {{ | |
| width: 80px; | |
| height: 80px; | |
| }} | |
| .number {{ | |
| font-size: 24px; | |
| }} | |
| .title {{ | |
| font-size: 22px; | |
| margin-bottom: 30px; | |
| }} | |
| }} | |
| </style> | |
| </head> | |
| <body> | |
| <div class="countdown-container"> | |
| <h1 class="title">Countdown to the Global Event Has Begun. Registrations are now open!</h1> | |
| <div class="countdown"> | |
| <div class="time-unit"> | |
| <div class="circle"> | |
| <svg> | |
| <circle class="circle-bg" cx="50" cy="50" r="45"></circle> | |
| <circle class="circle-progress" cx="50" cy="50" r="45" style="stroke-dashoffset: {days_offset:.1f};"></circle> | |
| </svg> | |
| <div class="number">{days:02d}</div> | |
| </div> | |
| <div class="label">Days</div> | |
| </div> | |
| <div class="time-unit"> | |
| <div class="circle"> | |
| <svg> | |
| <circle class="circle-bg" cx="50" cy="50" r="45"></circle> | |
| <circle class="circle-progress" cx="50" cy="50" r="45" style="stroke-dashoffset: {hours_offset:.1f};"></circle> | |
| </svg> | |
| <div class="number">{hours:02d}</div> | |
| </div> | |
| <div class="label">Hours</div> | |
| </div> | |
| <div class="time-unit"> | |
| <div class="circle"> | |
| <svg> | |
| <circle class="circle-bg" cx="50" cy="50" r="45"></circle> | |
| <circle class="circle-progress" cx="50" cy="50" r="45" style="stroke-dashoffset: {minutes_offset:.1f};"></circle> | |
| </svg> | |
| <div class="number">{minutes:02d}</div> | |
| </div> | |
| <div class="label">Minutes</div> | |
| </div> | |
| <div class="time-unit"> | |
| <div class="circle"> | |
| <svg> | |
| <circle class="circle-bg" cx="50" cy="50" r="45"></circle> | |
| <circle class="circle-progress" cx="50" cy="50" r="45" style="stroke-dashoffset: {seconds_offset:.1f};"></circle> | |
| </svg> | |
| <div class="number">{seconds:02d}</div> | |
| </div> | |
| <div class="label">Seconds</div> | |
| </div> | |
| </div> | |
| </div> | |
| <script> | |
| // Auto-refresh every second using meta refresh or JavaScript | |
| setTimeout(function(){{ | |
| location.reload(); | |
| }}, 1000); | |
| </script> | |
| </body> | |
| </html> | |
| """ | |
| def safe_add_to_dataset(registration_data, max_retries=5, retry_delay=3): | |
| """ | |
| Safely add new registration data with bulletproof error handling | |
| NEVER creates new datasets - only adds to existing ones | |
| """ | |
| try: | |
| logger.info("Starting new registration process") | |
| # Create new row with updated fields | |
| new_row = { | |
| "timestamp": registration_data["timestamp"], | |
| "full_name": registration_data["personal_info"]["full_name"], | |
| "email": registration_data["personal_info"]["email"], | |
| "hf_username": registration_data["personal_info"]["hf_username"], | |
| "gradio_usage": registration_data["personal_info"]["gradio_usage"], | |
| "track_interest": str(registration_data["participation"]["track_interest"]), | |
| "previous_participation": registration_data["participation"]["previous_participation"], | |
| "experience_level": registration_data["participation"]["experience_level"], | |
| "how_heard": registration_data["participation"]["how_heard"], | |
| "project_description": registration_data["additional"]["project_description"] or "", | |
| } | |
| logger.info("Created new row data") | |
| # Multi-attempt loading with different strategies | |
| existing_df = None | |
| load_successful = False | |
| for attempt in range(max_retries): | |
| logger.info(f"Loading attempt {attempt + 1}/{max_retries}") | |
| try: | |
| # Strategy 1: Direct parquet file access (most reliable) | |
| api = HfApi() | |
| files = api.list_repo_files(DATASET_NAME, repo_type="dataset") | |
| parquet_files = [f for f in files if f.endswith('.parquet') and 'train' in f] | |
| if parquet_files: | |
| logger.info(f"Found parquet file: {parquet_files[0]}") | |
| # Download to temporary location | |
| with tempfile.TemporaryDirectory() as temp_dir: | |
| parquet_file = api.hf_hub_download( | |
| repo_id=DATASET_NAME, | |
| filename=parquet_files[0], | |
| repo_type="dataset", | |
| cache_dir=temp_dir, | |
| force_download=True | |
| ) | |
| existing_df = pd.read_parquet(parquet_file) | |
| logger.info(f"Successfully loaded {len(existing_df)} existing rows") | |
| load_successful = True | |
| break | |
| else: | |
| logger.warning("No parquet files found") | |
| except Exception as load_error: | |
| logger.warning(f"Attempt {attempt + 1} failed: {str(load_error)[:100]}") | |
| if attempt < max_retries - 1: | |
| logger.info(f"Waiting {retry_delay} seconds before retry...") | |
| time.sleep(retry_delay) | |
| continue | |
| # CRITICAL SAFETY CHECK: Never proceed without existing data | |
| if not load_successful or existing_df is None: | |
| error_msg = "π¨ CRITICAL SAFETY ERROR: Could not load existing dataset after multiple attempts." | |
| logger.error(error_msg) | |
| logger.error("π¨ REFUSING to proceed to prevent data loss!") | |
| logger.error("π¨ Please check dataset manually or contact administrators.") | |
| return False, ( | |
| "β Registration temporarily unavailable due to technical issues. " | |
| "Please try again in a few minutes. If the problem persists, contact support." | |
| ) | |
| # Check for duplicates | |
| duplicate_check = existing_df[ | |
| (existing_df['email'].str.lower() == new_row['email'].lower()) | | |
| (existing_df['hf_username'].str.lower() == new_row['hf_username'].lower()) | |
| ] | |
| if len(duplicate_check) > 0: | |
| logger.warning("Duplicate registration attempt detected") | |
| return False, "β Error: This email or Hugging Face username is already registered." | |
| # Add new row safely | |
| combined_df = pd.concat([existing_df, pd.DataFrame([new_row])], ignore_index=True) | |
| logger.info(f"Combined data now has {len(combined_df)} rows (was {len(existing_df)})") | |
| # Create timestamped backup before upload | |
| backup_timestamp = int(time.time()) | |
| try: | |
| # Convert to Dataset and upload | |
| logger.info("Converting to HuggingFace Dataset format...") | |
| updated_dataset = Dataset.from_pandas(combined_df) | |
| # Create backup first | |
| backup_name = f"{DATASET_NAME}-auto-backup-{backup_timestamp}" | |
| logger.info(f"Creating backup: {backup_name}") | |
| updated_dataset.push_to_hub(backup_name, private=True) | |
| logger.info("Pushing to main dataset...") | |
| updated_dataset.push_to_hub(DATASET_NAME, private=True) | |
| logger.info("β Successfully saved new registration") | |
| logger.info(f"Total rows in dataset: {len(combined_df)}") | |
| # Quick verification | |
| time.sleep(2) | |
| try: | |
| verify_files = api.list_repo_files(DATASET_NAME, repo_type="dataset") | |
| logger.info("β Upload verification: Files updated successfully") | |
| except: | |
| logger.warning("β οΈ Could not verify upload (this may be normal)") | |
| return True, "Registration successful!" | |
| except Exception as upload_error: | |
| error_msg = str(upload_error).lower() | |
| if any(indicator in error_msg for indicator in ['rate limit', '429', 'too many requests']): | |
| logger.warning("π¨ Rate limit hit - registration system temporarily busy") | |
| return False, "β³ Registration temporarily unavailable due to high server load. Please try again in 10-15 minutes." | |
| else: | |
| logger.error(f"Upload failed: {upload_error}") | |
| return False, f"β Registration failed during upload: {str(upload_error)}" | |
| except Exception as e: | |
| logger.error(f"β Unexpected error in registration: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| return False, f"β Registration failed: {str(e)}" | |
| def verify_registration(email, hf_username): | |
| """ | |
| Verify if a user is registered by checking both email and HF username | |
| Returns registration details if both match | |
| """ | |
| try: | |
| # Validate inputs | |
| if not email or not email.strip(): | |
| return "β Please enter your email address" | |
| if not hf_username or not hf_username.strip(): | |
| return "β Please enter your Hugging Face username" | |
| logger.info(f"Verification attempt for email: {email}") | |
| # Load dataset | |
| try: | |
| dataset = load_dataset(DATASET_NAME, split="train") | |
| df = dataset.to_pandas() | |
| logger.info(f"Loaded dataset with {len(df)} registrations") | |
| except Exception as load_error: | |
| logger.error(f"Failed to load dataset: {load_error}") | |
| return "β Unable to verify registration at this time. Please try again later." | |
| # Search for exact match (both email AND username must match) | |
| email_lower = email.strip().lower() | |
| username_lower = hf_username.strip().lower() | |
| match = df[ | |
| (df['email'].str.lower() == email_lower) & | |
| (df['hf_username'].str.lower() == username_lower) | |
| ] | |
| if len(match) == 0: | |
| # Check if email exists with different username | |
| email_exists = df[df['email'].str.lower() == email_lower] | |
| username_exists = df[df['hf_username'].str.lower() == username_lower] | |
| if len(email_exists) > 0 and len(username_exists) == 0: | |
| return "β Email found but Hugging Face username doesn't match. Please check your username." | |
| elif len(username_exists) > 0 and len(email_exists) == 0: | |
| return "β Hugging Face username found but email doesn't match. Please check your email." | |
| else: | |
| return "β No registration found with this email and Hugging Face username combination." | |
| # Registration found - format the details | |
| registration = match.iloc[0] | |
| # Parse timestamp | |
| try: | |
| timestamp = pd.to_datetime(registration['timestamp']) | |
| reg_date = timestamp.strftime("%B %d, %Y at %I:%M %p UTC") | |
| except: | |
| reg_date = registration['timestamp'] | |
| # Format track interests | |
| track_interest = registration['track_interest'] | |
| if isinstance(track_interest, str): | |
| # Clean up the string representation of list | |
| track_interest = track_interest.strip("[]'\"").replace("'", "") | |
| result = f""" | |
| ## β Registration Confirmed! | |
| **Participant Details:** | |
| - **Full Name:** {registration['full_name']} | |
| - **Email:** {registration['email']} | |
| - **Hugging Face Username:** {registration['hf_username']} | |
| - **Registered On:** {reg_date} | |
| **Hackathon Participation:** | |
| - **Track Interest:** {track_interest} | |
| - **Gradio Usage:** {registration['gradio_usage']} | |
| - **Previous Participation:** {registration['previous_participation']} | |
| - **Experience Level:** {registration['experience_level']} | |
| - **How You Heard:** {registration['how_heard']} | |
| **Project Idea:** | |
| {registration['project_description'] if registration['project_description'] else '_No project description provided_'} | |
| --- | |
| **Next Steps:** | |
| - π API and Compute credits will be distributed before or during the hackathon | |
| - π¬ Join our Discord community channel `mcp-1st-birthday-officialπ`: https://discord.gg/92sEPT2Zhv | |
| - π§ Watch your email for important updates | |
| - π Start planning your project! | |
| """ | |
| logger.info(f"β Verification successful for {email}") | |
| return result | |
| except Exception as e: | |
| logger.error(f"Error during verification: {e}") | |
| import traceback | |
| traceback.print_exc() | |
| return f"β An error occurred during verification: {str(e)}" | |
| def submit_registration(full_name, email, hf_username, gradio_usage, | |
| track_interest, previous_participation, experience_level, how_heard, | |
| acknowledgment, project_description): | |
| """Process the registration form submission with enhanced validation""" | |
| # Enhanced validation | |
| if not full_name or not full_name.strip(): | |
| return "β Error: Please enter your full name" | |
| if not email or not email.strip(): | |
| return "β Error: Please enter your email address" | |
| if not hf_username or not hf_username.strip(): | |
| return "β Error: Please enter your Hugging Face username" | |
| if not gradio_usage: | |
| return "β Error: Please select how you're currently using Gradio" | |
| if not track_interest: | |
| return "β Error: Please select at least one track of interest" | |
| if not previous_participation: | |
| return "β Error: Please select your hackathon experience" | |
| if not experience_level: | |
| return "β Error: Please select your experience level" | |
| if not how_heard: | |
| return "β Error: Please select how you heard about this hackathon" | |
| if not acknowledgment: | |
| return "β Error: Please confirm your acknowledgment to participate" | |
| # Email format validation | |
| import re | |
| email_pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' | |
| if not re.match(email_pattern, email.strip()): | |
| return "β Error: Please enter a valid email address" | |
| # Process the registration data | |
| registration_data = { | |
| "timestamp": datetime.now().isoformat(), | |
| "personal_info": { | |
| "full_name": full_name.strip(), | |
| "email": email.strip().lower(), | |
| "hf_username": hf_username.strip(), | |
| "gradio_usage": gradio_usage, | |
| }, | |
| "participation": { | |
| "track_interest": track_interest, | |
| "previous_participation": previous_participation, | |
| "experience_level": experience_level, | |
| "how_heard": how_heard, | |
| }, | |
| "additional": { | |
| "project_description": project_description.strip() if project_description else None, | |
| } | |
| } | |
| # Save to Hugging Face dataset with bulletproof error handling | |
| success, message = safe_add_to_dataset(registration_data) | |
| if not success: | |
| return f"β Registration failed: {message}" | |
| return f"""β Registration Successful! | |
| Thank you, {full_name}! Your registration has been received and saved.<br> | |
| π§ You will receive information about API credits as we finalize sponsor partnerships.<br> | |
| π API and Compute credits will be distributed before or during the Hackathon starting date.<br> | |
| π¬ Be sure to **join the Huggingface organization** for regular updates on the hackathon and **to submit your entries**. Join our Discord community channel `mcp-1st-birthday-officialπ` for updates and support during the event: https://discord.gg/92sEPT2Zhv | |
| **See you at the hackathon! π**""" | |
| # Health check function | |
| def check_dataset_health(): | |
| """Check if the dataset is accessible and healthy""" | |
| try: | |
| api = HfApi() | |
| files = api.list_repo_files(DATASET_NAME, repo_type="dataset") | |
| parquet_files = [f for f in files if f.endswith('.parquet')] | |
| if parquet_files: | |
| logger.info(f"β Dataset health check passed - found {len(parquet_files)} parquet files") | |
| return True | |
| else: | |
| logger.warning("β οΈ Dataset health check: No parquet files found") | |
| return False | |
| except Exception as e: | |
| logger.error(f"β Dataset health check failed: {e}") | |
| return False | |
| # Initialize with health check | |
| logger.info("π Starting Gradio Hackathon Registration System - Winter 2025") | |
| logger.info(f"π Dataset: {DATASET_NAME}") | |
| if check_dataset_health(): | |
| logger.info("β System ready - dataset is healthy") | |
| else: | |
| logger.warning("β οΈ System starting with dataset health warnings") | |
| # Custom CSS for MCP theme | |
| custom_css = """ | |
| .gradio-container { | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif; | |
| } | |
| .header-gradient { | |
| color: #000000; | |
| } | |
| /* MCP-themed Submit Button Styling */ | |
| #gradient-submit-btn { | |
| background: #C8DDD7 !important; | |
| border: 2px solid #000000 !important; | |
| color: #000000 !important; | |
| font-weight: 600 !important; | |
| font-size: 18px !important; | |
| padding: 16px 32px !important; | |
| border-radius: 12px !important; | |
| box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important; | |
| transition: all 0.3s ease !important; | |
| text-transform: none !important; | |
| } | |
| #gradient-submit-btn:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25) !important; | |
| background: #B8CEC7 !important; | |
| } | |
| #gradient-submit-btn:active { | |
| transform: translateY(0px) !important; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2) !important; | |
| } | |
| /* Disabled state for the button */ | |
| #gradient-submit-btn:disabled { | |
| opacity: 0.6 !important; | |
| cursor: not-allowed !important; | |
| transform: none !important; | |
| } | |
| /* Verify button styling - Inverted MCP theme */ | |
| #verify-btn { | |
| background: #000000 !important; | |
| border: 2px solid #C8DDD7 !important; | |
| color: #C8DDD7 !important; | |
| font-weight: 600 !important; | |
| font-size: 16px !important; | |
| padding: 12px 24px !important; | |
| border-radius: 8px !important; | |
| box-shadow: 0 3px 10px rgba(200, 221, 215, 0.3) !important; | |
| transition: all 0.3s ease !important; | |
| } | |
| #verify-btn:hover { | |
| transform: translateY(-2px) !important; | |
| box-shadow: 0 5px 15px rgba(200, 221, 215, 0.5) !important; | |
| background: #1a1a1a !important; | |
| border-color: #D8EDE7 !important; | |
| } | |
| """ | |
| # Function to update countdown periodically | |
| def update_countdown(): | |
| """Update the countdown HTML""" | |
| return gr.HTML(get_countdown_html()) | |
| # Create the Gradio interface | |
| with gr.Blocks(title="Gradio Agents & MCP Hackathon - Winter 2025", css=custom_css, theme="ocean") as demo: | |
| # Header | |
| gr.Markdown(""" | |
| # MCP's 1st Birthday - Hosted by Anthropic and Gradio | |
| **Join our [Discord Community](https://discord.gg/92sEPT2Zhv) channel `mcp-1st-birthday-officialπ` for active support during the hackathon.** | |
| **NOTE: If the registration app says you are registered, then that information is correct and you don't need to send us anything else. NO CONFIRMATION EMAILS ARE SENT FROM OUR SIDE.** | |
| **π Event Dates:** November 14-30, 2025 (17 days, 3 weekends) | **π Prizes: $17,000+ USD in cash prizes** | **π» Location:** Online & Global | |
| **π FREE API & Compute Credits** (Details announced soon): | |
| - API credits from major AI providers | |
| - Access to latest and strongest LLMs | |
| - Compute resources for building your projects | |
| **The definitive Agents & MCP event is back!** OpenAI, Microsoft, Google DeepMind, and numerous startups have already adopted MCP. Join the community that launched the MCP developer movement. Participate and stand a chance to learn the latest AI technologies and also Win BIG! | |
| """) | |
| # Create countdown HTML component that will be updated | |
| countdown_display = gr.HTML(get_countdown_html()) | |
| gr.Markdown("---") | |
| # Create tabs | |
| with gr.Tabs(): | |
| # Registration Tab | |
| with gr.Tab("π Register"): | |
| with gr.Row(): | |
| with gr.Column(): | |
| # Personal Information Section | |
| gr.Markdown("## 1. Personal Information") | |
| full_name = gr.Textbox( | |
| label="Full Name *", | |
| placeholder="Your full name as you'd like it on certificates", | |
| max_lines=1 | |
| ) | |
| email = gr.Textbox( | |
| label="Email Address *", | |
| placeholder="Primary contact email (we'll send important updates here)", | |
| max_lines=1 | |
| ) | |
| hf_username = gr.Textbox( | |
| label="Hugging Face Username *", | |
| placeholder="Required for organization access and submissions", | |
| max_lines=1 | |
| ) | |
| gradio_usage = gr.Radio( | |
| label="How are you currently using Gradio? *", | |
| choices=[ | |
| "Professional work - My company uses Gradio", | |
| "Personal projects - Building side projects", | |
| "Academic/Research - University or research work", | |
| "Learning - New to Gradio, want to learn", | |
| "Not using yet - Interested to start" | |
| ], | |
| info="Helps us understand our community better" | |
| ) | |
| with gr.Column(): | |
| # Hackathon Participation Section | |
| gr.Markdown("## 2. Hackathon Participation") | |
| track_interest = gr.CheckboxGroup( | |
| label="Which track interests you most? *", | |
| choices=[ | |
| "Track 1: Building MCP", | |
| "Track 2: MCP in Action (Agents)", | |
| ] | |
| ) | |
| previous_participation = gr.Radio( | |
| label="Hackathon experience *", | |
| choices=[ | |
| "I participated in June 2025 Agents & MCP Hackathon", | |
| "I've done other AI hackathons before", | |
| "This is my first AI hackathon" | |
| ] | |
| ) | |
| experience_level = gr.Radio( | |
| label="Your experience with AI/Agents development *", | |
| choices=[ | |
| "Beginner - New to AI development", | |
| "Intermediate - Some AI projects", | |
| "Advanced - Regular AI developer", | |
| "Expert - Professional AI engineer" | |
| ] | |
| ) | |
| how_heard = gr.Dropdown( | |
| label="How did you hear about this hackathon? *", | |
| choices=[ | |
| "Hugging Face email/newsletter", | |
| "Twitter/X", | |
| "LinkedIn", | |
| "Discord", | |
| "From a colleague/friend", | |
| "YouTube", | |
| "Reddit", | |
| "Sponsor announcement", | |
| "I participated in June 2025", | |
| "Other" | |
| ] | |
| ) | |
| with gr.Row(): | |
| with gr.Column(): | |
| # Additional Information Section | |
| gr.Markdown("## 3. Additional Information") | |
| project_description = gr.Textbox( | |
| label="What type of project are you most excited to build?", | |
| placeholder="Brief description of your project idea or what interests you most", | |
| lines=3 | |
| ) | |
| with gr.Column(): | |
| # Acknowledgment Section | |
| gr.Markdown("## 4. Acknowledgment") | |
| acknowledgment = gr.Checkbox( | |
| label="Acknowledgment *", | |
| info="""I commit to actively participate and submit a project by November 30, 2025. I understand that API/compute credits are provided to support hackathon participation and should be used for building my hackathon project. I commit to using these credits responsibly during the event period.""", | |
| ) | |
| # Submit button | |
| submit_btn = gr.Button("π Register for Hackathon", variant="primary", size="lg", elem_id="gradient-submit-btn") | |
| # Output | |
| output = gr.Markdown() | |
| # Enhanced submit function | |
| def handle_registration_with_state(*args): | |
| try: | |
| result = submit_registration(*args) | |
| return result, gr.Button("π Register for Hackathon", interactive=True, variant="primary") | |
| except Exception as e: | |
| logger.error(f"Registration handling error: {e}") | |
| return f"β An unexpected error occurred: {str(e)}", gr.Button("π Register for Hackathon", interactive=True, variant="primary") | |
| # Click event with updated inputs | |
| submit_btn.click( | |
| fn=lambda *args: (gr.Button("β³ Processing Registration...", interactive=False, variant="secondary"), ""), | |
| inputs=[ | |
| full_name, email, hf_username, gradio_usage, | |
| track_interest, previous_participation, experience_level, how_heard, | |
| acknowledgment, project_description, | |
| ], | |
| outputs=[submit_btn, output], | |
| queue=False | |
| ).then( | |
| fn=handle_registration_with_state, | |
| inputs=[ | |
| full_name, email, hf_username, gradio_usage, | |
| track_interest, previous_participation, experience_level, how_heard, | |
| acknowledgment, project_description, | |
| ], | |
| outputs=[output, submit_btn], | |
| queue=True | |
| ) | |
| # Verification Tab | |
| with gr.Tab("π Verify Registration"): | |
| gr.Markdown(""" | |
| ## Check Your Registration Status | |
| Enter your email address and Hugging Face username to verify your registration and view your details. | |
| **Note:** Both email and username must match exactly for security purposes. | |
| """) | |
| with gr.Row(): | |
| with gr.Column(): | |
| verify_email = gr.Textbox( | |
| label="Email Address", | |
| placeholder="Enter your registered email", | |
| max_lines=1 | |
| ) | |
| verify_hf_username = gr.Textbox( | |
| label="Hugging Face Username", | |
| placeholder="Enter your registered HF username", | |
| max_lines=1 | |
| ) | |
| verify_btn = gr.Button("π Check Registration Status", variant="primary", size="lg", elem_id="verify-btn") | |
| with gr.Column(): | |
| gr.Markdown(""" | |
| ### Need Help? | |
| - Make sure you enter the **exact** email and username you used during registration | |
| - Both fields are **case-insensitive** but must match your registration | |
| - If you can't find your registration, try registering again or contact support | |
| **Support:** | |
| - Discord: https://discord.gg/92sEPT2Zhv | |
| - Email: [email protected] | |
| """) | |
| verify_output = gr.Markdown() | |
| # Verification click event | |
| verify_btn.click( | |
| fn=verify_registration, | |
| inputs=[verify_email, verify_hf_username], | |
| outputs=verify_output | |
| ) | |
| # Footer | |
| gr.Markdown(""" | |
| **Questions?** Join Huggingface [Discord](https://discord.gg/92sEPT2Zhv) or email: [email protected] | |
| """) | |
| # Set up a timer to update the countdown every second | |
| # This will refresh the countdown display periodically | |
| demo.load(update_countdown, None, countdown_display, every=1) | |
| if __name__ == "__main__": | |
| demo.launch(allowed_paths=["."]) |