import gradio as gr import pandas as pd from huggingface_hub import HfApi, hf_hub_download, upload_file import os from datetime import datetime from dotenv import load_dotenv import re from gradio_htmlplus import HTMLPlus load_dotenv() # CONFIGURATION DATASET_REPO_ID = "MCP-1st-Birthday/hackathon-community-voting" PROJECTS_FILE = "projects.csv" VOTES_FILE = "votes.csv" HF_TOKEN = os.getenv("HF_TOKEN") api = HfApi(token=HF_TOKEN) # Hackathon Structure TRACKS = ["Track 1: Building MCP", "Track 2: MCP in Action"] CATEGORIES = ["Enterprise", "Consumer", "Creative"] LEADERBOARD_VIEWS = [ "Track 1 (Overall)", "Track 2 (Enterprise)", "Track 2 (Consumer)", "Track 2 (Creative)", ] # DATA HANDLING FUNCTIONS def load_data(filename, repo_id): try: filepath = hf_hub_download( repo_id=repo_id, filename=filename, repo_type="dataset", token=HF_TOKEN ) return pd.read_csv(filepath, dtype={"track": str, "category": str}) except Exception: if filename == PROJECTS_FILE: return pd.DataFrame( columns=[ "space_url", "video_url", "track", "category", "submitted_by", "timestamp", ] ) elif filename == VOTES_FILE: return pd.DataFrame( columns=["space_url", "voted_by", "track", "category", "timestamp"] ) return pd.DataFrame() def save_data(df, filename, repo_id, commit_message): temp_path = f"./{filename}" df.to_csv(temp_path, index=False) upload_file( path_or_fileobj=temp_path, path_in_repo=filename, repo_id=repo_id, repo_type="dataset", token=HF_TOKEN, commit_message=commit_message, ) os.remove(temp_path) # --- CORE LOGIC --- def get_username(request: gr.Request): return ( request.request.session.get("oauth_info", {}) .get("userinfo", {}) .get("preferred_username") ) def parse_view_to_context(filter_view: str): if "Track 1" in filter_view: return TRACKS[0], "Overall" if "Track 2" in filter_view: if "Enterprise" in filter_view: return TRACKS[1], "Enterprise" if "Consumer" in filter_view: return TRACKS[1], "Consumer" if "Creative" in filter_view: return TRACKS[1], "Creative" return None, None def render_leaderboard(request: gr.Request, filter_view: str): username = get_username(request) projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID) votes_df = load_data(VOTES_FILE, DATASET_REPO_ID) vote_context_str = f"â„šī¸ Your vote will be cast in the **'{filter_view}'** context." if projects_df.empty: return ( "

No projects submitted yet.

", gr.update(choices=[], value=None), vote_context_str, gr.update(visible=False), ) track_filter, category_filter = parse_view_to_context(filter_view) display_df = pd.DataFrame() if track_filter == TRACKS[0]: track1_projects = projects_df[ projects_df["track"].str.contains(TRACKS[0], na=False) ].copy() if not track1_projects.empty: track1_projects["display_category"] = track1_projects[ "category" ].str.replace(";", " | ") track1_votes = votes_df[ (votes_df["track"] == TRACKS[0]) & (votes_df["category"] == "Overall") ] vote_counts = ( track1_votes.groupby("space_url").size().reset_index(name="votes") ) display_df = pd.merge( track1_projects, vote_counts, on="space_url", how="left" ).fillna(0) elif track_filter == TRACKS[1]: projects_df["track"] = projects_df["track"].str.split(";") exploded_tracks = projects_df.explode("track") exploded_tracks["category"] = exploded_tracks["category"].str.split(";") exploded_projects = exploded_tracks.explode("category") track2_filtered = exploded_projects[ (exploded_projects["track"] == track_filter) & (exploded_projects["category"] == category_filter) ] if not track2_filtered.empty: context_votes = votes_df[ (votes_df["track"] == track_filter) & (votes_df["category"] == category_filter) ] vote_counts = ( context_votes.groupby("space_url").size().reset_index(name="votes") ) display_df = pd.merge( track2_filtered, vote_counts, on="space_url", how="left" ).fillna(0) display_df["display_category"] = display_df["category"] if display_df.empty: return ( f"

No projects submitted for '{filter_view}' yet.

", gr.update(choices=[], value=None), vote_context_str, gr.update(visible=False), ) display_df["votes"] = display_df["votes"].astype(int) display_df["rank"] = ( display_df["votes"].rank(method="dense", ascending=False).astype(int) ) display_df = display_df.sort_values( by=["rank", "space_url"], ascending=True ).reset_index(drop=True) html = "
" trophies = {1: "đŸĨ‡", 2: "đŸĨˆ", 3: "đŸĨ‰"} for _, row in display_df.iterrows(): rank_num = row["rank"] rank_display = trophies.get(rank_num, f"#{rank_num}") space_name = re.sub(r"https://huggingface.co/spaces/", "", row["space_url"]) category_text = row["display_category"] submitter_name = row["submitted_by"] video_link_html = "" if pd.notna(row["video_url"]) and str(row["video_url"]).strip() and str(row["video_url"]).strip() != '0': video_link_html = ( f' | đŸŽŦ Video' ) action_button_html = "" if username and row["submitted_by"] == username: action_button_html = f'âœī¸' html += f"""
{rank_display}
{space_name} ({submitter_name})
🚀 Space {video_link_html} | Track: {row["track"].split(';')[0].split(':')[0]} | Categories: {category_text}
{row["votes"]} votes
{action_button_html}
""" html += "
" project_urls = display_df["space_url"].unique().tolist() return ( html, gr.update(choices=project_urls, value=None), vote_context_str, gr.update(visible=False), ) def submit_project( request: gr.Request, space_url, video_url, selected_tracks, selected_categories ): username = get_username(request) if not username: gr.Info("Error: You must be logged in to submit a project.") return gr.skip(), gr.skip(), gr.skip(), gr.skip() if not all([space_url, selected_tracks, selected_categories]): gr.Info( "Error: Space URL, at least one Track, and at least one Category are required." ) return gr.skip(), gr.skip(), gr.skip(), gr.skip() if not "huggingface.co/spaces/" in space_url: gr.Info("Error: Please enter a valid Hugging Face Space URL.") return gr.skip(), gr.skip(), gr.skip(), gr.skip() projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID) if space_url in projects_df["space_url"].values: gr.Info("Error: This project has already been submitted.") return gr.skip(), gr.skip(), gr.skip(), gr.skip() tracks_str = ";".join(selected_tracks) categories_str = ";".join(selected_categories) new_project = pd.DataFrame( [ { "space_url": space_url, "video_url": video_url, "track": tracks_str, "category": categories_str, "submitted_by": username, "timestamp": datetime.now().isoformat(), } ] ) updated_projects = pd.concat([projects_df, new_project], ignore_index=True) save_data( updated_projects, PROJECTS_FILE, DATASET_REPO_ID, f"Project submitted by {username}", ) gr.Info(f"✅ Success! Project '{space_url.split('/')[-1]}' submitted.") html, dropdown, context, edit_box_visibility = render_leaderboard( request, LEADERBOARD_VIEWS[0] ) return f"Last action: Success.", html, dropdown, context def cast_vote(request: gr.Request, project_to_vote, filter_view): username = get_username(request) if not username: gr.Info("Error: You must be logged in to vote.") return gr.skip(), gr.skip(), gr.skip(), gr.skip() # Return 4 values if not project_to_vote: gr.Info("Error: Please select a project to vote for.") return gr.skip(), gr.skip(), gr.skip(), gr.skip() # Return 4 values votes_df = load_data(VOTES_FILE, DATASET_REPO_ID) vote_track, vote_category = parse_view_to_context(filter_view) existing_vote = votes_df[ (votes_df["space_url"] == project_to_vote) & (votes_df["voted_by"] == username) & (votes_df["track"] == vote_track) & (votes_df["category"] == vote_category) ] if not existing_vote.empty: gr.Info( f"Notice: You have already voted for this project in the '{filter_view}' context." ) return gr.skip(), gr.skip(), gr.skip(), gr.skip() # Return 4 values new_vote = pd.DataFrame( [ { "space_url": project_to_vote, "voted_by": username, "track": vote_track, "category": vote_category, "timestamp": datetime.now().isoformat(), } ] ) updated_votes = pd.concat([votes_df, new_vote], ignore_index=True) save_data(updated_votes, VOTES_FILE, DATASET_REPO_ID, f"Vote cast by {username}") gr.Info( f"✅ Vote successfully cast for '{project_to_vote.split('/')[-1]}' in '{filter_view}'!" ) html, dropdown, context, _ = render_leaderboard(request, filter_view) return f"Last action: Success.", html, dropdown, context def edit_project( request: gr.Request, old_space_url: str, new_space_url: str, new_video_url: str, filter_view: str, ): username = get_username(request) if not username: gr.Info("Error: Authentication failed. Please log in again.") return gr.skip(), gr.skip(), gr.skip(), gr.update(visible=False) projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID) votes_df = load_data(VOTES_FILE, DATASET_REPO_ID) project_index = projects_df[ (projects_df["space_url"] == old_space_url) & (projects_df["submitted_by"] == username) ].index if project_index.empty: gr.Info( "Error: You do not have permission to edit this project, or it no longer exists." ) return gr.skip(), gr.skip(), gr.skip(), gr.update(visible=False) projects_df.loc[project_index, "space_url"] = new_space_url projects_df.loc[project_index, "video_url"] = new_video_url save_data( projects_df, PROJECTS_FILE, DATASET_REPO_ID, f"Project edited by {username}: {old_space_url} -> {new_space_url}", ) if old_space_url != new_space_url: votes_df.loc[votes_df["space_url"] == old_space_url, "space_url"] = ( new_space_url ) save_data( votes_df, VOTES_FILE, DATASET_REPO_ID, f"Vote URLs updated for project edit by {username}", ) gr.Info("✅ Project URLs updated successfully! Votes have been preserved.") html, dropdown, context, _ = render_leaderboard(request, filter_view) return "Project edited.", html, dropdown, context, gr.update(visible=False) # --- THEME --- theme = gr.themes.Default( primary_hue="blue", secondary_hue="yellow", neutral_hue="neutral" ).set( body_background_fill="*neutral_100", body_background_fill_dark="*neutral_800", body_text_color="*neutral_700", body_text_color_dark="*neutral_200", body_text_size="1.1em", code_background_fill="*neutral_100", code_background_fill_dark="*neutral_800", shadow_drop="2px 2px 4px *neutral_400", block_label_background_fill="*neutral_100", block_label_background_fill_dark="*neutral_800", block_label_text_color="*neutral_700", block_label_text_color_dark="*neutral_200", block_title_text_color="*primary_700", block_title_text_color_dark="*primary_300", panel_background_fill="*neutral_50", panel_background_fill_dark="*neutral_900", panel_border_color="*neutral_200", panel_border_color_dark="*neutral_700", checkbox_border_color="*neutral_300", checkbox_border_color_dark="*neutral_600", input_background_fill="white", input_background_fill_dark="*neutral_800", input_border_color="*neutral_300", input_border_color_dark="*neutral_600", slider_color="*primary_500", slider_color_dark="*primary_400", button_primary_background_fill="*primary_600", button_primary_background_fill_dark="*primary_500", button_primary_background_fill_hover="*primary_700", button_primary_background_fill_hover_dark="*primary_600", button_primary_text_color="white", button_primary_text_color_dark="white", button_secondary_background_fill="*secondary_400", button_secondary_background_fill_dark="*secondary_500", button_secondary_background_fill_hover="*secondary_500", button_secondary_background_fill_hover_dark="*secondary_600", button_secondary_text_color="*neutral_700", button_secondary_text_color_dark="*neutral_200", button_cancel_background_fill="*neutral_200", button_cancel_background_fill_dark="*neutral_700", button_cancel_background_fill_hover="*neutral_300", button_cancel_background_fill_hover_dark="*neutral_600", button_cancel_text_color="*neutral_700", button_cancel_text_color_dark="*neutral_200", ) # --- GRADIO INTERFACE --- with gr.Blocks(theme=theme, title="Hackathon Community Choice") as app: gr.Markdown("# 🏆 Hackathon Community Choice Award") gr.Markdown( "Vote for your favorite hackathon projects! Please log in to participate." ) gr.LoginButton() with gr.Tabs() as tabs: with gr.TabItem("🏆 Leaderboard & Vote", id=0): leaderboard_filter = gr.Radio( LEADERBOARD_VIEWS, label="Select Leaderboard View", value=LEADERBOARD_VIEWS[0], ) with gr.Group(visible=False) as edit_group: gr.Markdown("### Edit Project URLs") edit_old_url = gr.Textbox(label="Original Space URL", interactive=False) edit_new_url = gr.Textbox(label="New Space URL") edit_new_video_url = gr.Textbox(label="New Video URL") with gr.Row(): save_edit_button = gr.Button("Save Changes", variant="primary") cancel_edit_button = gr.Button("Cancel") with gr.Row(): with gr.Column(scale=2): leaderboard_html = HTMLPlus( "Please log in to load...", selectable_elements=[".edit-button"] ) with gr.Column(scale=1): gr.Markdown("### Cast Your Vote") vote_context_display = gr.Markdown() vote_status = gr.Markdown() project_dropdown = gr.Dropdown( label="Select a Project to Vote For", interactive=True ) vote_button = gr.Button( "👍 Vote for Selected Project", variant="primary" ) with gr.TabItem("🚀 Submit Your Project", id=1): gr.Markdown("### Register Your Project") submission_status = gr.Markdown() space_url_input = gr.Textbox( label="Your Hugging Face Space URL", placeholder="https://huggingface.co/spaces/...", ) # NEW: Video URL now has "(Optional)" in the label video_url_input = gr.Textbox( label="Your Demo Video URL (Optional)", placeholder="https://www.youtube.com/watch?v=...", ) track_checkboxes = gr.CheckboxGroup(TRACKS, label="Select Your Track(s)") category_checkboxes = gr.CheckboxGroup( CATEGORIES, label="Select Your Category(s)" ) submit_button = gr.Button("Submit Project", variant="primary") # --- Event Handling --- def handle_page_load(request: gr.Request, filter_view: str): username = get_username(request) if username: gr.Info(f"Welcome, {username}!") return render_leaderboard(request, filter_view) def cancel_edit(): return gr.update(visible=False), "", "", "" def handle_leaderboard_click(evt: gr.SelectData): if evt.index == ".edit-button": project_to_edit_url = evt.value.get("url") if project_to_edit_url: projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID) project_data = projects_df[ projects_df["space_url"] == project_to_edit_url ].iloc[0] return ( gr.update(visible=True), project_data["space_url"], project_data["space_url"], project_data["video_url"], ) return gr.skip(), gr.skip(), gr.skip(), gr.skip() app.load( fn=handle_page_load, inputs=[leaderboard_filter], outputs=[leaderboard_html, project_dropdown, vote_context_display, edit_group], ) leaderboard_filter.change( fn=render_leaderboard, inputs=[leaderboard_filter], outputs=[leaderboard_html, project_dropdown, vote_context_display, edit_group], ) submit_button.click( fn=submit_project, inputs=[ space_url_input, video_url_input, track_checkboxes, category_checkboxes, ], outputs=[ submission_status, leaderboard_html, project_dropdown, vote_context_display, ], ) vote_button.click( fn=cast_vote, inputs=[project_dropdown, leaderboard_filter], outputs=[vote_status, leaderboard_html, project_dropdown, vote_context_display], ) leaderboard_html.select( fn=handle_leaderboard_click, outputs=[edit_group, edit_old_url, edit_new_url, edit_new_video_url], ) save_edit_button.click( fn=edit_project, inputs=[edit_old_url, edit_new_url, edit_new_video_url, leaderboard_filter], outputs=[ submission_status, leaderboard_html, project_dropdown, vote_context_display, edit_group, ], ) cancel_edit_button.click( fn=cancel_edit, outputs=[edit_group, edit_old_url, edit_new_url, edit_new_video_url], ) app.launch()