elismasilva commited on
Commit
2dcfb8d
Β·
1 Parent(s): 3621d34

first commit

Browse files
Files changed (3) hide show
  1. .gitignore +17 -0
  2. app.py +286 -0
  3. requirements.txt +4 -0
.gitignore ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ .eggs/
2
+ dist/
3
+ .vscode/
4
+ *.pyc
5
+ __pycache__/
6
+ *.py[cod]
7
+ *$py.class
8
+ __tmp/*
9
+ *.pyi
10
+ .mypycache
11
+ .ruff_cache
12
+ node_modules
13
+ backend/**/templates/
14
+ README_TEMPLATE.md
15
+ .env
16
+ *.pem
17
+ post.md
app.py ADDED
@@ -0,0 +1,286 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import pandas as pd
3
+ from huggingface_hub import HfApi, hf_hub_download, upload_file
4
+ import os
5
+ from datetime import datetime
6
+ from dotenv import load_dotenv
7
+ import re
8
+
9
+ load_dotenv()
10
+
11
+ # CONFIGURATION
12
+ DATASET_REPO_ID = "elismasilva/hackathon-community-voting"
13
+ PROJECTS_FILE = "projects.csv"
14
+ VOTES_FILE = "votes.csv"
15
+
16
+ # Authentication with the token from Space secrets
17
+ HF_TOKEN = os.getenv("HF_TOKEN")
18
+ api = HfApi(token=HF_TOKEN)
19
+
20
+ # Hackathon Structure
21
+ TRACKS = ["Track 1: Building MCP", "Track 2: MCP in Action"]
22
+ CATEGORIES = ["Enterprise", "Consumer", "Creative"]
23
+ LEADERBOARD_VIEWS = ["Track 1 (Overall)", "Track 2 (Enterprise)", "Track 2 (Consumer)", "Track 2 (Creative)"]
24
+
25
+
26
+ # DATA HANDLING FUNCTIONS
27
+
28
+ def load_data(filename, repo_id):
29
+ """Loads a CSV from the dataset, or creates an empty DataFrame if it doesn't exist."""
30
+ try:
31
+ filepath = hf_hub_download(repo_id=repo_id, filename=filename, repo_type="dataset", token=HF_TOKEN)
32
+ return pd.read_csv(filepath)
33
+ except Exception:
34
+ if filename == PROJECTS_FILE:
35
+ return pd.DataFrame(columns=["space_url", "video_url", "track", "category", "submitted_by", "timestamp"])
36
+ elif filename == VOTES_FILE:
37
+ return pd.DataFrame(columns=["space_url", "voted_by", "timestamp"])
38
+ return pd.DataFrame()
39
+
40
+ def save_data(df, filename, repo_id, commit_message):
41
+ """Saves a DataFrame as a CSV to the dataset."""
42
+ temp_path = f"./{filename}"
43
+ df.to_csv(temp_path, index=False)
44
+ upload_file(
45
+ path_or_fileobj=temp_path,
46
+ path_in_repo=filename,
47
+ repo_id=repo_id,
48
+ repo_type="dataset",
49
+ token=HF_TOKEN,
50
+ commit_message=commit_message,
51
+ )
52
+ os.remove(temp_path)
53
+
54
+ # CORE LOGIC
55
+
56
+ def render_leaderboard(request: gr.Request, filter_view: str):
57
+ """
58
+ Renders the leaderboard HTML. It gets the user identity from the request object.
59
+ This is a "pure" rendering function without side-effects.
60
+ """
61
+ 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
62
+ if not username:
63
+ return "<h3>Please log in to view and vote on projects!</h3>", gr.update(choices=[], value=None)
64
+
65
+ projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
66
+ votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
67
+
68
+ if projects_df.empty:
69
+ return "<h3>No projects have been submitted yet. Be the first!</h3>", gr.update(choices=[], value=None)
70
+
71
+ vote_counts = votes_df['space_url'].value_counts().reset_index()
72
+ vote_counts.columns = ['space_url', 'votes']
73
+
74
+ full_leaderboard_df = pd.merge(projects_df, vote_counts, on="space_url", how="left").fillna(0)
75
+ full_leaderboard_df['votes'] = full_leaderboard_df['votes'].astype(int)
76
+
77
+ # Filtering logic...
78
+ if filter_view == "Track 1 (Overall)":
79
+ filtered_df = full_leaderboard_df[full_leaderboard_df['track'] == TRACKS[0]]
80
+ elif filter_view == "Track 2 (Enterprise)":
81
+ filtered_df = full_leaderboard_df[(full_leaderboard_df['track'] == TRACKS[1]) & (full_leaderboard_df['category'] == 'Enterprise')]
82
+ elif filter_view == "Track 2 (Consumer)":
83
+ filtered_df = full_leaderboard_df[(full_leaderboard_df['track'] == TRACKS[1]) & (full_leaderboard_df['category'] == 'Consumer')]
84
+ elif filter_view == "Track 2 (Creative)":
85
+ filtered_df = full_leaderboard_df[(full_leaderboard_df['track'] == TRACKS[1]) & (full_leaderboard_df['category'] == 'Creative')]
86
+ else:
87
+ filtered_df = full_leaderboard_df[full_leaderboard_df['track'] == TRACKS[0]]
88
+
89
+ if filtered_df.empty:
90
+ return f"<h3>No projects submitted for this view ('{filter_view}') yet.</h3>", gr.update(choices=[], value=None)
91
+
92
+ filtered_df = filtered_df.sort_values(by="votes", ascending=False).reset_index(drop=True)
93
+ html = "<div>" # HTML generation remains the same...
94
+ trophies = {0: "πŸ₯‡", 1: "πŸ₯ˆ", 2: "πŸ₯‰"}
95
+ for index, row in filtered_df.iterrows():
96
+ rank = trophies.get(index, f"<b>#{index + 1}</b>")
97
+ space_name = re.sub(r'https://huggingface.co/spaces/', '', row['space_url'])
98
+ html += f"""
99
+ <div style="display: flex; align-items: center; padding: 12px; border-bottom: 1px solid #eee; gap: 15px;">
100
+ <div style="font-size: 24px; width: 50px;">{rank}</div>
101
+ <div style="flex-grow: 1;">
102
+ <div style="font-weight: bold; font-size: 16px;">{space_name}</div>
103
+ <div style="font-size: 12px; color: #555;">
104
+ <a href="{row['space_url']}" target="_blank">πŸš€ Space</a> |
105
+ <a href="{row['video_url']}" target="_blank">🎬 Video</a> |
106
+ <span style="color: #777;">Track: {row['track'].split(':')[0]} | Category: {row['category']}</span>
107
+ </div>
108
+ </div>
109
+ <div style="font-size: 20px; font-weight: bold; color: #3B82F6;">{row['votes']} votes</div>
110
+ </div>
111
+ """
112
+ html += "</div>"
113
+ project_urls = filtered_df['space_url'].tolist()
114
+ return html, gr.update(choices=project_urls, value=None)
115
+
116
+
117
+ def submit_project(request: gr.Request, space_url, video_url, track, category):
118
+ """Adds a new project."""
119
+ 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
120
+ if not username:
121
+ gr.Info("Error: You must be logged in to submit a project.")
122
+ return gr.skip(), gr.skip(), gr.skip()
123
+ if not all([space_url, video_url, track, category]):
124
+ gr.Info("Error: All fields are required.")
125
+ return gr.skip(), gr.skip(), gr.skip()
126
+ if not ("huggingface.co/spaces/" in space_url and ("youtube.com/" in video_url or "youtu.be/" in video_url)):
127
+ gr.Info("Error: Please enter valid URLs from Hugging Face Spaces and YouTube.")
128
+ return gr.skip(), gr.skip(), gr.skip()
129
+
130
+ projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
131
+
132
+ if space_url in projects_df['space_url'].values:
133
+ gr.Info("Error: This project has already been submitted.")
134
+ return gr.skip(), gr.skip(), gr.skip()
135
+
136
+ new_project = pd.DataFrame([{"space_url": space_url, "video_url": video_url, "track": track, "category": category, "submitted_by": username, "timestamp": datetime.now().isoformat()}])
137
+ updated_projects = pd.concat([projects_df, new_project], ignore_index=True)
138
+ save_data(updated_projects, PROJECTS_FILE, DATASET_REPO_ID, f"Project submitted by {username}")
139
+
140
+ gr.Info(f"βœ… Success! Project '{space_url.split('/')[-1]}' submitted.")
141
+
142
+ # After submission, refresh the leaderboard
143
+ html, dropdown_update = render_leaderboard(request, LEADERBOARD_VIEWS[0])
144
+ return f"Last action: Success.", html, dropdown_update
145
+
146
+
147
+ def cast_vote(request: gr.Request, project_to_vote, filter_view):
148
+ """Registers a vote."""
149
+ 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
150
+ if not username:
151
+ gr.Info("Error: You must be logged in to vote.")
152
+ return gr.skip(), gr.skip(), gr.skip()
153
+ if not project_to_vote:
154
+ gr.Info("Error: Please select a project to vote for.")
155
+ return gr.skip(), gr.skip(), gr.skip()
156
+
157
+ votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
158
+
159
+ if not votes_df[(votes_df['space_url'] == project_to_vote) & (votes_df['voted_by'] == username)].empty:
160
+ gr.Info(f"Notice: You have already voted for '{project_to_vote.split('/')[-1]}'.")
161
+ return gr.skip(), gr.skip(), gr.skip()
162
+
163
+ new_vote = pd.DataFrame([{"space_url": project_to_vote, "voted_by": username, "timestamp": datetime.now().isoformat()}])
164
+ updated_votes = pd.concat([votes_df, new_vote], ignore_index=True)
165
+ save_data(updated_votes, VOTES_FILE, DATASET_REPO_ID, f"Vote cast by {username}")
166
+
167
+ gr.Info(f"βœ… Vote successfully cast for '{project_to_vote.split('/')[-1]}'!")
168
+
169
+ # After voting, refresh the leaderboard with the current filter
170
+ html, dropdown_update = render_leaderboard(request, filter_view)
171
+ return f"Last action: Success.", html, dropdown_update
172
+
173
+ theme = gr.themes.Default(
174
+ primary_hue='blue',
175
+ secondary_hue='yellow',
176
+ neutral_hue='neutral'
177
+ ).set(
178
+ body_background_fill='*neutral_100',
179
+ body_background_fill_dark='*neutral_800',
180
+ body_text_color='*neutral_700',
181
+ body_text_color_dark='*neutral_200',
182
+ body_text_size='1.1em',
183
+ code_background_fill='*neutral_100',
184
+ code_background_fill_dark='*neutral_800',
185
+ shadow_drop='2px 2px 4px *neutral_400',
186
+ block_label_background_fill='*neutral_100',
187
+ block_label_background_fill_dark='*neutral_800',
188
+ block_label_text_color='*neutral_700',
189
+ block_label_text_color_dark='*neutral_200',
190
+ block_title_text_color='*primary_700',
191
+ block_title_text_color_dark='*primary_300',
192
+ panel_background_fill='*neutral_50',
193
+ panel_background_fill_dark='*neutral_900',
194
+ panel_border_color='*neutral_200',
195
+ panel_border_color_dark='*neutral_700',
196
+ checkbox_border_color='*neutral_300',
197
+ checkbox_border_color_dark='*neutral_600',
198
+ input_background_fill='white',
199
+ input_background_fill_dark='*neutral_800',
200
+ input_border_color='*neutral_300',
201
+ input_border_color_dark='*neutral_600',
202
+ slider_color='*primary_500',
203
+ slider_color_dark='*primary_400',
204
+ button_primary_background_fill='*primary_600',
205
+ button_primary_background_fill_dark='*primary_500',
206
+ button_primary_background_fill_hover='*primary_700',
207
+ button_primary_background_fill_hover_dark='*primary_600',
208
+ button_primary_text_color='white',
209
+ button_primary_text_color_dark='white',
210
+ button_secondary_background_fill='*secondary_400',
211
+ button_secondary_background_fill_dark='*secondary_500',
212
+ button_secondary_background_fill_hover='*secondary_500',
213
+ button_secondary_background_fill_hover_dark='*secondary_600',
214
+ button_secondary_text_color='*neutral_700',
215
+ button_secondary_text_color_dark='*neutral_200',
216
+ button_cancel_background_fill='*neutral_200',
217
+ button_cancel_background_fill_dark='*neutral_700',
218
+ button_cancel_background_fill_hover='*neutral_300',
219
+ button_cancel_background_fill_hover_dark='*neutral_600',
220
+ button_cancel_text_color='*neutral_700',
221
+ button_cancel_text_color_dark='*neutral_200'
222
+ )
223
+
224
+ with gr.Blocks(title="Hackathon Community Choice") as app:
225
+ gr.Markdown("# πŸ† Hackathon Community Choice Award")
226
+ gr.Markdown("Vote for your favorite hackathon projects! Please log in to participate.")
227
+
228
+ gr.LoginButton()
229
+
230
+ with gr.Tabs() as tabs:
231
+ with gr.TabItem("πŸ† Leaderboard & Vote", id=0):
232
+ leaderboard_filter = gr.Radio(LEADERBOARD_VIEWS, label="Select Leaderboard View", value=LEADERBOARD_VIEWS[0])
233
+ with gr.Row():
234
+ with gr.Column(scale=2):
235
+ leaderboard_html = gr.HTML("Please log in to load...")
236
+ with gr.Column(scale=1):
237
+ gr.Markdown("### Cast Your Vote!")
238
+ vote_status = gr.Markdown()
239
+ project_dropdown = gr.Dropdown(label="Select a Project to Vote For", interactive=True)
240
+ vote_button = gr.Button("πŸ‘ Vote for Selected Project", variant="primary")
241
+
242
+ with gr.TabItem("πŸš€ Submit Your Project", id=1):
243
+ gr.Markdown("### Register Your Project")
244
+ submission_status = gr.Markdown()
245
+ space_url_input = gr.Textbox(label="Your Hugging Face Space URL", placeholder="https://huggingface.co/spaces/...")
246
+ video_url_input = gr.Textbox(label="Your Demo Video URL (YouTube)", placeholder="https://www.youtube.com/watch?v=...")
247
+ track_radio = gr.Radio(TRACKS, label="Select Your Track")
248
+ category_dropdown = gr.Dropdown(CATEGORIES, label="Select Your Category")
249
+ submit_button = gr.Button("Submit Project", variant="primary")
250
+
251
+ # Event Handling
252
+
253
+ def handle_page_load(request: gr.Request, filter_view: str):
254
+ """Called only on app.load. Gives welcome message and renders."""
255
+ 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
256
+ if username:
257
+ gr.Info(f"Welcome, {username}!")
258
+ return render_leaderboard(request, filter_view)
259
+
260
+ # The page load triggers the first render and welcome message.
261
+ app.load(
262
+ fn=handle_page_load,
263
+ inputs=[leaderboard_filter],
264
+ outputs=[leaderboard_html, project_dropdown]
265
+ )
266
+
267
+ # When the user changes the filter, re-render the leaderboard (no welcome message).
268
+ leaderboard_filter.change(
269
+ fn=render_leaderboard,
270
+ inputs=[leaderboard_filter],
271
+ outputs=[leaderboard_html, project_dropdown]
272
+ )
273
+
274
+ submit_button.click(
275
+ fn=submit_project,
276
+ inputs=[space_url_input, video_url_input, track_radio, category_dropdown],
277
+ outputs=[submission_status, leaderboard_html, project_dropdown]
278
+ )
279
+
280
+ vote_button.click(
281
+ fn=cast_vote,
282
+ inputs=[project_dropdown, leaderboard_filter],
283
+ outputs=[vote_status, leaderboard_html, project_dropdown]
284
+ )
285
+
286
+ app.launch(theme=theme)
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ #gradio==5.50.0
2
+ #gradio[oauth]==5.50.0
3
+ pandas
4
+ python-dotenv