|
|
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() |
|
|
|
|
|
|
|
|
DATASET_REPO_ID = "elismasilva/hackathon-community-voting" |
|
|
PROJECTS_FILE = "projects.csv" |
|
|
VOTES_FILE = "votes.csv" |
|
|
|
|
|
|
|
|
HF_TOKEN = os.getenv("HF_TOKEN") |
|
|
api = HfApi(token=HF_TOKEN) |
|
|
|
|
|
|
|
|
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)"] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
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>" |
|
|
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.") |
|
|
|
|
|
|
|
|
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]}'!") |
|
|
|
|
|
|
|
|
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") |
|
|
|
|
|
|
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
app.load( |
|
|
fn=handle_page_load, |
|
|
inputs=[leaderboard_filter], |
|
|
outputs=[leaderboard_html, project_dropdown] |
|
|
) |
|
|
|
|
|
|
|
|
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) |