Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import json | |
| import pandas as pd | |
| import os | |
| import shutil | |
| import datetime | |
| # Paths | |
| DATA_DIR = "data" | |
| DATA_FILE = os.path.join(DATA_DIR, "teamup_data.json") | |
| BACKUP_DIR = os.path.join(DATA_DIR, "backup") | |
| os.makedirs(DATA_DIR, exist_ok=True) | |
| ADMIN_CODE = os.getenv("ADMIN_CODE", "") | |
| # Init data file | |
| if not os.path.exists(DATA_FILE) or os.path.getsize(DATA_FILE) == 0: | |
| with open(DATA_FILE, "w") as f: | |
| json.dump([], f) | |
| # Country list | |
| ALL_COUNTRIES = sorted(list(set([ | |
| "United States of America", "United Kingdom", "India", "France", "Germany", "Canada", "Australia", "Japan", "Brazil", "Mexico", | |
| # Add or reduce to most relevant countries only for dropdown | |
| ] + [ # Fallback: full list | |
| "Afghanistan", "Albania", "Algeria", "Andorra", "Angola", "Argentina", "Armenia", "Austria", "Azerbaijan", | |
| "Bangladesh", "Belgium", "Bhutan", "Bolivia", "Bosnia and Herzegovina", "Botswana", "Bulgaria", "Cambodia", | |
| "Chile", "China", "Colombia", "Croatia", "Cuba", "Czech Republic", "Denmark", "Dominican Republic", "Ecuador", | |
| "Egypt", "Estonia", "Ethiopia", "Finland", "Greece", "Hungary", "Iceland", "Indonesia", "Iran", "Iraq", "Ireland", | |
| "Israel", "Italy", "Jordan", "Kenya", "Kuwait", "Latvia", "Lithuania", "Luxembourg", "Malaysia", "Malta", "Morocco", | |
| "Nepal", "Netherlands", "New Zealand", "Nigeria", "Norway", "Pakistan", "Peru", "Philippines", "Poland", "Portugal", | |
| "Qatar", "Romania", "Russia", "Saudi Arabia", "Serbia", "Singapore", "Slovakia", "Slovenia", "South Africa", "South Korea", | |
| "Spain", "Sri Lanka", "Sweden", "Switzerland", "Thailand", "Tunisia", "Turkey", "Ukraine", "United Arab Emirates", "Vietnam", | |
| "Zambia", "Zimbabwe" | |
| ]))) | |
| # Backup | |
| def backup_data(): | |
| os.makedirs(BACKUP_DIR, exist_ok=True) | |
| timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S') | |
| backup_file = os.path.join(BACKUP_DIR, f'teamup_data_backup_{timestamp}.json') | |
| shutil.copy(DATA_FILE, backup_file) | |
| # Safe save | |
| def atomic_save(data, path): | |
| tmp_path = path + ".tmp" | |
| with open(tmp_path, "w") as f: | |
| json.dump(data, f, indent=2) | |
| os.replace(tmp_path, path) | |
| # Profile submission | |
| def submit_profile(name, discord, city, country, address, looking, onlinecheck, languages, laptop, robot, skills, describe3, experience, idea): | |
| if not discord or not city or not country or not laptop or not robot: | |
| return "β Please fill in all required fields." | |
| if not languages or not isinstance(languages, list): | |
| return "β Please select at least one language." | |
| # Normalize input | |
| city = city.strip().title() | |
| country = country.strip().title() | |
| discord = discord.strip() | |
| with open(DATA_FILE, "r") as f: | |
| data = json.load(f) | |
| for entry in data: | |
| if entry["Discord"].lower() == discord.lower(): | |
| entry.update({ | |
| "Name": name, | |
| "City": city, | |
| "Country": country, | |
| "Address": address, | |
| "Looking for Team": looking, | |
| "How?": onlinecheck, | |
| "Languages": languages, | |
| "Laptop": laptop, | |
| "Robot": robot, | |
| "Skills": skills, | |
| "Description": describe3, | |
| "Experience": experience, | |
| "Project Idea": idea | |
| }) | |
| break | |
| else: | |
| data.append({ | |
| "Name": name, | |
| "Discord": discord, | |
| "City": city, | |
| "Country": country, | |
| "Address": address, | |
| "Looking for Team": looking, | |
| "How?": onlinecheck, | |
| "Languages": languages, | |
| "Laptop": laptop, | |
| "Robot": robot, | |
| "Skills": skills, | |
| "Description": describe3, | |
| "Experience": experience, | |
| "Project Idea": idea | |
| }) | |
| try: | |
| atomic_save(data, DATA_FILE) | |
| backup_data() | |
| return "β Profile saved!" | |
| except Exception as e: | |
| return f"β Failed to save: {e}" | |
| # City dropdown update | |
| def update_city_filter(country): | |
| with open(DATA_FILE, "r") as f: | |
| data = json.load(f) | |
| df = pd.DataFrame(data) | |
| if country != "All": | |
| df = df[df["Country"].str.title() == country.title()] | |
| cities = sorted(set(df["City"].dropna().unique())) | |
| return gr.update(choices=["All"] + cities, value="All") | |
| # Filter participants | |
| def filter_by_fields(selected_country, selected_city, selected_language): | |
| with open(DATA_FILE, "r") as f: | |
| data = json.load(f) | |
| df = pd.DataFrame(data) | |
| if df.empty: | |
| return "<p>No data available.</p>" | |
| df["Languages"] = df["Languages"].apply(lambda x: ", ".join(x) if isinstance(x, list) else str(x)) | |
| if selected_country != "All": | |
| df = df[df["Country"].str.title() == selected_country.title()] | |
| if selected_city != "All": | |
| df = df[df["City"].str.title() == selected_city.title()] | |
| if selected_language != "All": | |
| df = df[df["Languages"].str.contains(selected_language)] | |
| if "Address" in df.columns: | |
| df.drop(columns=["Address"], inplace=True) | |
| if df.empty: | |
| return "<p>No participants match your filters.</p>" | |
| html = '<h3 style="margin-top:20px;">π Find your matching Teammates & Register your team <a href="https://forms.gle/gJEMGD4CEA2emhD18" target="_blank">here</a>ππ</h3>' | |
| html += "<table style='width:100%; border-collapse: collapse;'>" | |
| # Header row | |
| html += "<tr>" | |
| for col in df.columns: | |
| html += f"<th style='border: 1px solid #ccc; padding: 6px;'>{col}</th>" | |
| html += "</tr>" | |
| # Data rows | |
| for _, row in df.iterrows(): | |
| html += "<tr>" | |
| for col in df.columns: | |
| val = row[col] | |
| if col == "Discord": | |
| val = f"<a href='https://discord.com/users/{val}' target='_blank'>{val}</a>" | |
| html += f"<td style='border: 1px solid #eee; padding: 6px;'>{val}</td>" | |
| html += "</tr>" | |
| html += "</table>" | |
| return html | |
| # Gradio Interface | |
| with gr.Blocks(css=".gr-dropdown { max-height: 100px; overflow-y: auto; font-size: 12px; }") as demo: | |
| gr.Markdown("# π LeRobot Worldwide Hackathon - Team-Up Dashboard") | |
| gr.Markdown("1. Submit or update your profile to find matching teammates and contact them on Discord. (Required fields marked with *.) ") | |
| with gr.Row(): | |
| with gr.Column(): | |
| name = gr.Text(label="Name") | |
| discord = gr.Text(label="π€ Discord Username *") | |
| city = gr.Text(label="π City *") | |
| country = gr.Dropdown(label="π Country *", choices=ALL_COUNTRIES, value="France") | |
| address = gr.Text(label="Address (optional)") | |
| looking = gr.Radio(["Yes", "No"], label="π Looking for a team?") | |
| onlinecheck = gr.Radio(["Participate Online", "Join a Local Hackathon"], label="π I will...") | |
| languages = gr.CheckboxGroup(choices=["English", "French", "Spanish", "German", "Portuguese", "Chinese", "Arabic", "Hindi"], label="Languages Spoken *") | |
| laptop = gr.Text(label="π» Laptop Setup *") | |
| robot = gr.Text(label="π€ Robot Setup *") | |
| skills = gr.Text(label="π§ Skills (comma separated)") | |
| describe3 = gr.Text(label="3 Words That Describe You") | |
| experience = gr.Dropdown(choices=["Beginner", "Intermediate", "Advanced"], label="Experience Level", value="Beginner") | |
| idea = gr.Textbox(label="Project Idea (optional)") | |
| submit_btn = gr.Button("Submit or Update β ") | |
| status = gr.Textbox(label="", interactive=False) | |
| with gr.Column(): | |
| gr.Markdown("π― 2. Choose your preferences to find your teammates (country, city or language)") | |
| country_filter = gr.Dropdown(label="Filter by Country", choices=["All"] + ALL_COUNTRIES, value="All") | |
| city_filter = gr.Dropdown(label="Filter by City", choices=["All"], value="All") | |
| language_filter = gr.Dropdown(label="Filter by Language", choices=["All", "English", "French", "Spanish", "German", "Portuguese", "Chinese", "Arabic", "Hindi"], value="All") | |
| table_html = gr.HTML(label="Matching Participants") | |
| submit_btn.click( | |
| fn=submit_profile, | |
| inputs=[name, discord, city, country, address, looking, onlinecheck, languages, laptop, robot, skills, describe3, experience, idea], | |
| outputs=[status] | |
| ).then( | |
| fn=filter_by_fields, | |
| inputs=[country_filter, city_filter, language_filter], | |
| outputs=[table_html] | |
| ) | |
| country_filter.change(fn=update_city_filter, inputs=[country_filter], outputs=[city_filter]) | |
| for dropdown in [country_filter, city_filter, language_filter]: | |
| dropdown.change(fn=filter_by_fields, inputs=[country_filter, city_filter, language_filter], outputs=[table_html]) | |
| gr.Markdown("---\n### π‘οΈ Admin Panel (delete by Discord)") | |
| admin_discord = gr.Text(label="Discord Username") | |
| admin_code = gr.Text(label="Admin Code", type="password") | |
| del_btn = gr.Button("Delete Profile") | |
| del_status = gr.Textbox(label="Status", interactive=False) | |
| del_btn.click(delete_by_discord, inputs=[admin_discord, admin_code], outputs=del_status) | |
| # π CSV Download Section (admin-only) | |
| gr.Markdown("---\n### π₯ Admin Export CSV") | |
| export_code = gr.Text(label="Admin Code", type="password") | |
| download_btn = gr.Button("Generate and Download CSV") | |
| download_file = gr.File(label="CSV Export", interactive=False) | |
| download_btn.click(fn=download_csv, inputs=[export_code], outputs=[download_file]) | |
| demo.launch() | |