elismasilva's picture
first commit
2dcfb8d
raw
history blame
13.6 kB
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
load_dotenv()
# CONFIGURATION
DATASET_REPO_ID = "elismasilva/hackathon-community-voting"
PROJECTS_FILE = "projects.csv"
VOTES_FILE = "votes.csv"
# Authentication with the token from Space secrets
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):
"""Loads a CSV from the dataset, or creates an empty DataFrame if it doesn't exist."""
try:
filepath = hf_hub_download(repo_id=repo_id, filename=filename, repo_type="dataset", token=HF_TOKEN)
return pd.read_csv(filepath)
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", "timestamp"])
return pd.DataFrame()
def save_data(df, filename, repo_id, commit_message):
"""Saves a DataFrame as a CSV to the dataset."""
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 render_leaderboard(request: gr.Request, filter_view: str):
"""
Renders the leaderboard HTML. It gets the user identity from the request object.
This is a "pure" rendering function without side-effects.
"""
username = request.request.session['oauth_info']['userinfo']['preferred_username'] if hasattr(request.request, "session") and len(request.request.session) > 0 and 'userinfo' in request.request.session['oauth_info'] else None
if not username:
return "<h3>Please log in to view and vote on projects!</h3>", gr.update(choices=[], value=None)
projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
if projects_df.empty:
return "<h3>No projects have been submitted yet. Be the first!</h3>", gr.update(choices=[], value=None)
vote_counts = votes_df['space_url'].value_counts().reset_index()
vote_counts.columns = ['space_url', 'votes']
full_leaderboard_df = pd.merge(projects_df, vote_counts, on="space_url", how="left").fillna(0)
full_leaderboard_df['votes'] = full_leaderboard_df['votes'].astype(int)
# Filtering logic...
if filter_view == "Track 1 (Overall)":
filtered_df = full_leaderboard_df[full_leaderboard_df['track'] == TRACKS[0]]
elif filter_view == "Track 2 (Enterprise)":
filtered_df = full_leaderboard_df[(full_leaderboard_df['track'] == TRACKS[1]) & (full_leaderboard_df['category'] == 'Enterprise')]
elif filter_view == "Track 2 (Consumer)":
filtered_df = full_leaderboard_df[(full_leaderboard_df['track'] == TRACKS[1]) & (full_leaderboard_df['category'] == 'Consumer')]
elif filter_view == "Track 2 (Creative)":
filtered_df = full_leaderboard_df[(full_leaderboard_df['track'] == TRACKS[1]) & (full_leaderboard_df['category'] == 'Creative')]
else:
filtered_df = full_leaderboard_df[full_leaderboard_df['track'] == TRACKS[0]]
if filtered_df.empty:
return f"<h3>No projects submitted for this view ('{filter_view}') yet.</h3>", gr.update(choices=[], value=None)
filtered_df = filtered_df.sort_values(by="votes", ascending=False).reset_index(drop=True)
html = "<div>" # HTML generation remains the same...
trophies = {0: "πŸ₯‡", 1: "πŸ₯ˆ", 2: "πŸ₯‰"}
for index, row in filtered_df.iterrows():
rank = trophies.get(index, f"<b>#{index + 1}</b>")
space_name = re.sub(r'https://huggingface.co/spaces/', '', row['space_url'])
html += f"""
<div style="display: flex; align-items: center; padding: 12px; border-bottom: 1px solid #eee; gap: 15px;">
<div style="font-size: 24px; width: 50px;">{rank}</div>
<div style="flex-grow: 1;">
<div style="font-weight: bold; font-size: 16px;">{space_name}</div>
<div style="font-size: 12px; color: #555;">
<a href="{row['space_url']}" target="_blank">πŸš€ Space</a> |
<a href="{row['video_url']}" target="_blank">🎬 Video</a> |
<span style="color: #777;">Track: {row['track'].split(':')[0]} | Category: {row['category']}</span>
</div>
</div>
<div style="font-size: 20px; font-weight: bold; color: #3B82F6;">{row['votes']} votes</div>
</div>
"""
html += "</div>"
project_urls = filtered_df['space_url'].tolist()
return html, gr.update(choices=project_urls, value=None)
def submit_project(request: gr.Request, space_url, video_url, track, category):
"""Adds a new project."""
username = request.request.session['oauth_info']['userinfo']['preferred_username'] if hasattr(request.request, "session") and len(request.request.session) > 0 and 'userinfo' in request.request.session['oauth_info'] else None
if not username:
gr.Info("Error: You must be logged in to submit a project.")
return gr.skip(), gr.skip(), gr.skip()
if not all([space_url, video_url, track, category]):
gr.Info("Error: All fields are required.")
return gr.skip(), gr.skip(), gr.skip()
if not ("huggingface.co/spaces/" in space_url and ("youtube.com/" in video_url or "youtu.be/" in video_url)):
gr.Info("Error: Please enter valid URLs from Hugging Face Spaces and YouTube.")
return 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()
new_project = pd.DataFrame([{"space_url": space_url, "video_url": video_url, "track": track, "category": category, "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.")
# After submission, refresh the leaderboard
html, dropdown_update = render_leaderboard(request, LEADERBOARD_VIEWS[0])
return f"Last action: Success.", html, dropdown_update
def cast_vote(request: gr.Request, project_to_vote, filter_view):
"""Registers a vote."""
username = request.request.session['oauth_info']['userinfo']['preferred_username'] if hasattr(request.request, "session") and len(request.request.session) > 0 and 'userinfo' in request.request.session['oauth_info'] else None
if not username:
gr.Info("Error: You must be logged in to vote.")
return gr.skip(), gr.skip(), gr.skip()
if not project_to_vote:
gr.Info("Error: Please select a project to vote for.")
return gr.skip(), gr.skip(), gr.skip()
votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
if not votes_df[(votes_df['space_url'] == project_to_vote) & (votes_df['voted_by'] == username)].empty:
gr.Info(f"Notice: You have already voted for '{project_to_vote.split('/')[-1]}'.")
return gr.skip(), gr.skip(), gr.skip()
new_vote = pd.DataFrame([{"space_url": project_to_vote, "voted_by": username, "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]}'!")
# After voting, refresh the leaderboard with the current filter
html, dropdown_update = render_leaderboard(request, filter_view)
return f"Last action: Success.", html, dropdown_update
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'
)
with gr.Blocks(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.Row():
with gr.Column(scale=2):
leaderboard_html = gr.HTML("Please log in to load...")
with gr.Column(scale=1):
gr.Markdown("### Cast Your Vote!")
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/...")
video_url_input = gr.Textbox(label="Your Demo Video URL (YouTube)", placeholder="https://www.youtube.com/watch?v=...")
track_radio = gr.Radio(TRACKS, label="Select Your Track")
category_dropdown = gr.Dropdown(CATEGORIES, label="Select Your Category")
submit_button = gr.Button("Submit Project", variant="primary")
# Event Handling
def handle_page_load(request: gr.Request, filter_view: str):
"""Called only on app.load. Gives welcome message and renders."""
username = request.request.session['oauth_info']['userinfo']['preferred_username'] if hasattr(request.request, "session") and len(request.request.session) > 0 and 'userinfo' in request.request.session['oauth_info'] else None
if username:
gr.Info(f"Welcome, {username}!")
return render_leaderboard(request, filter_view)
# The page load triggers the first render and welcome message.
app.load(
fn=handle_page_load,
inputs=[leaderboard_filter],
outputs=[leaderboard_html, project_dropdown]
)
# When the user changes the filter, re-render the leaderboard (no welcome message).
leaderboard_filter.change(
fn=render_leaderboard,
inputs=[leaderboard_filter],
outputs=[leaderboard_html, project_dropdown]
)
submit_button.click(
fn=submit_project,
inputs=[space_url_input, video_url_input, track_radio, category_dropdown],
outputs=[submission_status, leaderboard_html, project_dropdown]
)
vote_button.click(
fn=cast_vote,
inputs=[project_dropdown, leaderboard_filter],
outputs=[vote_status, leaderboard_html, project_dropdown]
)
app.launch(theme=theme)