elismasilva's picture
update
1281eb7
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 = "elismasilva/hackathon-builders-choice"
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 (
"<h3>No projects submitted yet.</h3>",
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"<h3>No projects submitted for '{filter_view}' yet.</h3>",
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 = "<div>"
trophies = {1: "πŸ₯‡", 2: "πŸ₯ˆ", 3: "πŸ₯‰"}
for _, row in display_df.iterrows():
rank_num = row["rank"]
rank_display = trophies.get(rank_num, f"<b>#{rank_num}</b>")
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' | <a href="{row["video_url"]}" target="_blank">🎬 Video</a>'
)
action_button_html = ""
if username and row["submitted_by"] == username:
action_button_html = f'<a href="#" class="edit-button" data-url="{row["space_url"]}" style="text-decoration: none; font-size: 20px; margin-left: 15px;" title="Edit this project">✏️</a>'
html += f"""
<div style="display: flex; align-items: center; padding: 12px; border-bottom: 1px solid #eee; gap: 15px;" data-space-url="{row['space_url']}">
<div style="font-size: 24px; width: 50px;">{rank_display}</div>
<div style="flex-grow: 1;">
<div style="font-weight: bold; font-size: 16px;">{space_name} <span style="font-weight: normal; color: #888; font-size: 14px;">({submitter_name})</span></div>
<div style="font-size: 12px; color: #555;">
<a href="{row["space_url"]}" target="_blank">πŸš€ Space</a>
{video_link_html}
| <span style="color: #777;">Track: {row["track"].split(';')[0].split(':')[0]} | Categories: {category_text}</span>
</div>
</div>
<div style="font-size: 20px; font-weight: bold; color: #3B82F6;">{row["votes"]} votes</div>
{action_button_html}
</div>
"""
html += "</div>"
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 Builders' Choice") as app:
gr.Markdown("# πŸ† Hackathon Builders' Choice")
gr.Markdown("An unofficial poll for participants to appreciate each other's work. Log in to submit your project and vote!")
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()