elismasilva commited on
Commit
3a820ea
Β·
1 Parent(s): 97cba6f

fixing errors

Browse files
Files changed (1) hide show
  1. app.py +370 -120
app.py CHANGED
@@ -5,7 +5,7 @@ import os
5
  from datetime import datetime
6
  from dotenv import load_dotenv
7
  import re
8
- from gradio_htmlplus import HTMLPlus # Import the custom component
9
 
10
  load_dotenv()
11
 
@@ -19,105 +19,179 @@ api = HfApi(token=HF_TOKEN)
19
  # Hackathon Structure
20
  TRACKS = ["Track 1: Building MCP", "Track 2: MCP in Action"]
21
  CATEGORIES = ["Enterprise", "Consumer", "Creative"]
22
- LEADERBOARD_VIEWS = ["Track 1 (Overall)", "Track 2 (Enterprise)", "Track 2 (Consumer)", "Track 2 (Creative)"]
 
 
 
 
 
23
 
24
- # --- DATA HANDLING FUNCTIONS ---
 
25
  def load_data(filename, repo_id):
26
- """Loads a CSV from the dataset, or creates an empty DataFrame if it doesn't exist."""
27
  try:
28
- filepath = hf_hub_download(repo_id=repo_id, filename=filename, repo_type="dataset", token=HF_TOKEN)
29
- return pd.read_csv(filepath, dtype={'track': str, 'category': str})
 
 
30
  except Exception:
31
  if filename == PROJECTS_FILE:
32
- return pd.DataFrame(columns=["space_url", "video_url", "track", "category", "submitted_by", "timestamp"])
 
 
 
 
 
 
 
 
 
33
  elif filename == VOTES_FILE:
34
- return pd.DataFrame(columns=["space_url", "voted_by", "track", "category", "timestamp"])
 
 
35
  return pd.DataFrame()
36
 
 
37
  def save_data(df, filename, repo_id, commit_message):
38
- """Saves a DataFrame as a CSV to the dataset."""
39
  temp_path = f"./{filename}"
40
  df.to_csv(temp_path, index=False)
41
  upload_file(
42
- path_or_fileobj=temp_path, path_in_repo=filename, repo_id=repo_id, repo_type="dataset",
43
- token=HF_TOKEN, commit_message=commit_message,
 
 
 
 
44
  )
45
  os.remove(temp_path)
46
 
 
47
  # --- CORE LOGIC ---
48
 
 
49
  def get_username(request: gr.Request):
50
- """Safely gets the username using the user's original, working method."""
51
- return request.request.session.get('oauth_info', {}).get('userinfo', {}).get('preferred_username')
 
 
 
 
52
 
53
  def parse_view_to_context(filter_view: str):
54
- """Helper to get track and category from the leaderboard view string."""
55
- if "Track 1" in filter_view: return TRACKS[0], "Overall"
56
  if "Track 2" in filter_view:
57
- if "Enterprise" in filter_view: return TRACKS[1], "Enterprise"
58
- if "Consumer" in filter_view: return TRACKS[1], "Consumer"
59
- if "Creative" in filter_view: return TRACKS[1], "Creative"
 
 
 
60
  return None, None
61
 
 
62
  def render_leaderboard(request: gr.Request, filter_view: str):
63
- """Renders the leaderboard HTML, including a conditional Edit button."""
64
  username = get_username(request)
65
  projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
66
  votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
67
  vote_context_str = f"ℹ️ Your vote will be cast in the **'{filter_view}'** context."
68
 
69
  if projects_df.empty:
70
- return "<h3>No projects submitted yet.</h3>", gr.update(choices=[], value=None), vote_context_str, gr.update(visible=False)
 
 
 
 
 
71
 
72
  track_filter, category_filter = parse_view_to_context(filter_view)
73
  display_df = pd.DataFrame()
74
 
75
  if track_filter == TRACKS[0]:
76
- track1_projects = projects_df[projects_df['track'].str.contains(TRACKS[0], na=False)].copy()
 
 
77
  if not track1_projects.empty:
78
- track1_projects['display_category'] = track1_projects['category'].str.replace(';', ' | ')
79
- track1_votes = votes_df[(votes_df['track'] == TRACKS[0]) & (votes_df['category'] == "Overall")]
80
- vote_counts = track1_votes.groupby('space_url').size().reset_index(name='votes')
81
- display_df = pd.merge(track1_projects, vote_counts, on='space_url', how='left').fillna(0)
82
-
 
 
 
 
 
 
 
83
  elif track_filter == TRACKS[1]:
84
- projects_df['track'] = projects_df['track'].str.split(';')
85
- exploded_tracks = projects_df.explode('track')
86
- exploded_tracks['category'] = exploded_tracks['category'].str.split(';')
87
- exploded_projects = exploded_tracks.explode('category')
88
- track2_filtered = exploded_projects[(exploded_projects['track'] == track_filter) & (exploded_projects['category'] == category_filter)]
 
 
 
89
  if not track2_filtered.empty:
90
- context_votes = votes_df[(votes_df['track'] == track_filter) & (votes_df['category'] == category_filter)]
91
- vote_counts = context_votes.groupby('space_url').size().reset_index(name='votes')
92
- display_df = pd.merge(track2_filtered, vote_counts, on='space_url', how='left').fillna(0)
93
- display_df['display_category'] = display_df['category']
94
-
 
 
 
 
 
 
 
95
  if display_df.empty:
96
- return f"<h3>No projects submitted for '{filter_view}' yet.</h3>", gr.update(choices=[], value=None), vote_context_str, gr.update(visible=False)
 
 
 
 
 
 
 
 
 
 
97
 
98
- display_df['votes'] = display_df['votes'].astype(int)
99
- display_df = display_df.sort_values(by="votes", ascending=False).reset_index(drop=True)
 
100
 
101
  html = "<div>"
102
- trophies = {0: "πŸ₯‡", 1: "πŸ₯ˆ", 2: "πŸ₯‰"}
103
- for index, row in display_df.iterrows():
104
- rank = trophies.get(index, f"<b>#{index + 1}</b>")
105
- space_name = re.sub(r'https://huggingface.co/spaces/', '', row["space_url"])
 
106
  category_text = row["display_category"]
107
-
 
 
 
 
 
 
 
108
  action_button_html = ""
109
  if username and row["submitted_by"] == username:
110
  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>'
111
 
112
  html += f"""
113
  <div style="display: flex; align-items: center; padding: 12px; border-bottom: 1px solid #eee; gap: 15px;" data-space-url="{row['space_url']}">
114
- <div style="font-size: 24px; width: 50px;">{rank}</div>
115
  <div style="flex-grow: 1;">
116
- <div style="font-weight: bold; font-size: 16px;">{space_name}</div>
117
  <div style="font-size: 12px; color: #555;">
118
- <a href="{row["space_url"]}" target="_blank">πŸš€ Space</a> |
119
- <a href="{row["video_url"]}" target="_blank">🎬 Video</a> |
120
- <span style="color: #777;">Track: {row["track"].split(';')[0].split(':')[0]} | Categories: {category_text}</span>
121
  </div>
122
  </div>
123
  <div style="font-size: 20px; font-weight: bold; color: #3B82F6;">{row["votes"]} votes</div>
@@ -125,30 +199,64 @@ def render_leaderboard(request: gr.Request, filter_view: str):
125
  </div>
126
  """
127
  html += "</div>"
128
-
129
- project_urls = display_df['space_url'].unique().tolist()
130
- return html, gr.update(choices=project_urls, value=None), vote_context_str, gr.update(visible=False)
 
 
 
 
 
131
 
132
 
133
- def submit_project(request: gr.Request, space_url, video_url, selected_tracks, selected_categories):
 
 
134
  username = get_username(request)
135
  if not username:
136
  gr.Info("Error: You must be logged in to submit a project.")
137
  return gr.skip(), gr.skip(), gr.skip(), gr.skip()
138
- if not all([space_url, video_url, selected_tracks, selected_categories]):
139
- gr.Info("Error: All fields are required, including at least one track and one category.")
 
 
 
 
 
 
 
140
  return gr.skip(), gr.skip(), gr.skip(), gr.skip()
 
141
  projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
142
- if space_url in projects_df['space_url'].values:
143
  gr.Info("Error: This project has already been submitted.")
144
  return gr.skip(), gr.skip(), gr.skip(), gr.skip()
 
145
  tracks_str = ";".join(selected_tracks)
146
  categories_str = ";".join(selected_categories)
147
- 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()}])
 
 
 
 
 
 
 
 
 
 
 
148
  updated_projects = pd.concat([projects_df, new_project], ignore_index=True)
149
- save_data(updated_projects, PROJECTS_FILE, DATASET_REPO_ID, f"Project submitted by {username}")
 
 
 
 
 
150
  gr.Info(f"βœ… Success! Project '{space_url.split('/')[-1]}' submitted.")
151
- html, dropdown, context, edit_box_visibility = render_leaderboard(request, LEADERBOARD_VIEWS[0])
 
 
152
  return f"Last action: Success.", html, dropdown, context
153
 
154
 
@@ -156,76 +264,157 @@ def cast_vote(request: gr.Request, project_to_vote, filter_view):
156
  username = get_username(request)
157
  if not username:
158
  gr.Info("Error: You must be logged in to vote.")
159
- return gr.skip(), gr.skip(), gr.skip()
160
  if not project_to_vote:
161
  gr.Info("Error: Please select a project to vote for.")
162
- return gr.skip(), gr.skip(), gr.skip()
 
163
  votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
164
  vote_track, vote_category = parse_view_to_context(filter_view)
165
- 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)]
 
 
 
 
 
166
  if not existing_vote.empty:
167
- gr.Info(f"Notice: You have already voted for this project in the '{filter_view}' context.")
168
- return gr.skip(), gr.skip(), gr.skip()
169
- new_vote = pd.DataFrame([{"space_url": project_to_vote, "voted_by": username, "track": vote_track, "category": vote_category, "timestamp": datetime.now().isoformat()}])
 
 
 
 
 
 
 
 
 
 
 
 
 
170
  updated_votes = pd.concat([votes_df, new_vote], ignore_index=True)
171
  save_data(updated_votes, VOTES_FILE, DATASET_REPO_ID, f"Vote cast by {username}")
172
- gr.Info(f"βœ… Vote successfully cast for '{project_to_vote.split('/')[-1]}' in '{filter_view}'!")
173
- html, dropdown, context = render_leaderboard(request, filter_view)
174
- return f"Last action: Success.", html, dropdown
 
 
 
 
175
 
176
 
177
- def edit_project(request: gr.Request, old_space_url: str, new_space_url: str, new_video_url: str, filter_view: str):
 
 
 
 
 
 
178
  username = get_username(request)
179
  if not username:
180
  gr.Info("Error: Authentication failed. Please log in again.")
181
  return gr.skip(), gr.skip(), gr.skip(), gr.update(visible=False)
182
  projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
183
  votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
184
- project_index = projects_df[(projects_df['space_url'] == old_space_url) & (projects_df['submitted_by'] == username)].index
 
 
 
185
  if project_index.empty:
186
- gr.Info("Error: You do not have permission to edit this project, or it no longer exists.")
 
 
187
  return gr.skip(), gr.skip(), gr.skip(), gr.update(visible=False)
188
- projects_df.loc[project_index, 'space_url'] = new_space_url
189
- projects_df.loc[project_index, 'video_url'] = new_video_url
190
- save_data(projects_df, PROJECTS_FILE, DATASET_REPO_ID, f"Project edited by {username}: {old_space_url} -> {new_space_url}")
 
 
 
 
 
191
  if old_space_url != new_space_url:
192
- votes_df.loc[votes_df['space_url'] == old_space_url, 'space_url'] = new_space_url
193
- save_data(votes_df, VOTES_FILE, DATASET_REPO_ID, f"Vote URLs updated for project edit by {username}")
 
 
 
 
 
 
 
194
  gr.Info("βœ… Project URLs updated successfully! Votes have been preserved.")
195
  html, dropdown, context, _ = render_leaderboard(request, filter_view)
196
  return "Project edited.", html, dropdown, context, gr.update(visible=False)
197
 
 
198
  # --- THEME ---
199
- theme = gr.themes.Default(primary_hue='blue', secondary_hue='yellow', neutral_hue='neutral').set(
200
- body_background_fill='*neutral_100', body_background_fill_dark='*neutral_800', body_text_color='*neutral_700',
201
- body_text_color_dark='*neutral_200', body_text_size='1.1em', code_background_fill='*neutral_100',
202
- code_background_fill_dark='*neutral_800', shadow_drop='2px 2px 4px *neutral_400', block_label_background_fill='*neutral_100',
203
- block_label_background_fill_dark='*neutral_800', block_label_text_color='*neutral_700', block_label_text_color_dark='*neutral_200',
204
- block_title_text_color='*primary_700', block_title_text_color_dark='*primary_300', panel_background_fill='*neutral_50',
205
- panel_background_fill_dark='*neutral_900', panel_border_color='*neutral_200', panel_border_color_dark='*neutral_700',
206
- checkbox_border_color='*neutral_300', checkbox_border_color_dark='*neutral_600', input_background_fill='white',
207
- input_background_fill_dark='*neutral_800', input_border_color='*neutral_300', input_border_color_dark='*neutral_600',
208
- slider_color='*primary_500', slider_color_dark='*primary_400', button_primary_background_fill='*primary_600',
209
- button_primary_background_fill_dark='*primary_500', button_primary_background_fill_hover='*primary_700',
210
- button_primary_background_fill_hover_dark='*primary_600', button_primary_text_color='white',
211
- button_primary_text_color_dark='white', button_secondary_background_fill='*secondary_400',
212
- button_secondary_background_fill_dark='*secondary_500', button_secondary_background_fill_hover='*secondary_500',
213
- button_secondary_background_fill_hover_dark='*secondary_600', button_secondary_text_color='*neutral_700',
214
- button_secondary_text_color_dark='*neutral_200', button_cancel_background_fill='*neutral_200',
215
- button_cancel_background_fill_dark='*neutral_700', button_cancel_background_fill_hover='*neutral_300',
216
- button_cancel_background_fill_hover_dark='*neutral_600', button_cancel_text_color='*neutral_700',
217
- button_cancel_text_color_dark='*neutral_200'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
218
  )
219
 
220
  # --- GRADIO INTERFACE ---
221
  with gr.Blocks(theme=theme, title="Hackathon Community Choice") as app:
222
  gr.Markdown("# πŸ† Hackathon Community Choice Award")
223
- gr.Markdown("Vote for your favorite hackathon projects! Please log in to participate.")
 
 
224
  gr.LoginButton()
225
 
226
  with gr.Tabs() as tabs:
227
  with gr.TabItem("πŸ† Leaderboard & Vote", id=0):
228
- leaderboard_filter = gr.Radio(LEADERBOARD_VIEWS, label="Select Leaderboard View", value=LEADERBOARD_VIEWS[0])
 
 
 
 
229
  with gr.Group(visible=False) as edit_group:
230
  gr.Markdown("### Edit Project URLs")
231
  edit_old_url = gr.Textbox(label="Original Space URL", interactive=False)
@@ -234,27 +423,39 @@ with gr.Blocks(theme=theme, title="Hackathon Community Choice") as app:
234
  with gr.Row():
235
  save_edit_button = gr.Button("Save Changes", variant="primary")
236
  cancel_edit_button = gr.Button("Cancel")
237
-
238
  with gr.Row():
239
  with gr.Column(scale=2):
240
  leaderboard_html = HTMLPlus(
241
- "Please log in to load...",
242
- selectable_elements=[".edit-button"]
243
  )
244
  with gr.Column(scale=1):
245
  gr.Markdown("### Cast Your Vote")
246
  vote_context_display = gr.Markdown()
247
  vote_status = gr.Markdown()
248
- project_dropdown = gr.Dropdown(label="Select a Project to Vote For", interactive=True)
249
- vote_button = gr.Button("πŸ‘ Vote for Selected Project", variant="primary")
250
-
 
 
 
 
251
  with gr.TabItem("πŸš€ Submit Your Project", id=1):
252
  gr.Markdown("### Register Your Project")
253
  submission_status = gr.Markdown()
254
- space_url_input = gr.Textbox(label="Your Hugging Face Space URL", placeholder="https://huggingface.co/spaces/...")
255
- video_url_input = gr.Textbox(label="Your Demo Video URL (YouTube)", placeholder="https://www.youtube.com/watch?v=...")
 
 
 
 
 
 
 
256
  track_checkboxes = gr.CheckboxGroup(TRACKS, label="Select Your Track(s)")
257
- category_checkboxes = gr.CheckboxGroup(CATEGORIES, label="Select Your Category(s)")
 
 
258
  submit_button = gr.Button("Submit Project", variant="primary")
259
 
260
  # --- Event Handling ---
@@ -269,21 +470,70 @@ with gr.Blocks(theme=theme, title="Hackathon Community Choice") as app:
269
 
270
  def handle_leaderboard_click(evt: gr.SelectData):
271
  if evt.index == ".edit-button":
272
- project_to_edit_url = evt.value.get("url")
273
  if project_to_edit_url:
274
  projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
275
- project_data = projects_df[projects_df['space_url'] == project_to_edit_url].iloc[0]
276
- return gr.update(visible=True), project_data['space_url'], project_data['space_url'], project_data['video_url']
 
 
 
 
 
 
 
277
  return gr.skip(), gr.skip(), gr.skip(), gr.skip()
278
 
279
- app.load(fn=handle_page_load, inputs=[leaderboard_filter], outputs=[leaderboard_html, project_dropdown, vote_context_display, edit_group])
280
- leaderboard_filter.change(fn=render_leaderboard, inputs=[leaderboard_filter], outputs=[leaderboard_html, project_dropdown, vote_context_display, edit_group])
281
- 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])
282
- vote_button.click(fn=cast_vote, inputs=[project_dropdown, leaderboard_filter], outputs=[vote_status, leaderboard_html, project_dropdown])
283
-
284
- leaderboard_html.select(fn=handle_leaderboard_click, outputs=[edit_group, edit_old_url, edit_new_url, edit_new_video_url])
285
-
286
- 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])
287
- cancel_edit_button.click(fn=cancel_edit, outputs=[edit_group, edit_old_url, edit_new_url, edit_new_video_url])
288
-
289
- app.launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5
  from datetime import datetime
6
  from dotenv import load_dotenv
7
  import re
8
+ from gradio_htmlplus import HTMLPlus
9
 
10
  load_dotenv()
11
 
 
19
  # Hackathon Structure
20
  TRACKS = ["Track 1: Building MCP", "Track 2: MCP in Action"]
21
  CATEGORIES = ["Enterprise", "Consumer", "Creative"]
22
+ LEADERBOARD_VIEWS = [
23
+ "Track 1 (Overall)",
24
+ "Track 2 (Enterprise)",
25
+ "Track 2 (Consumer)",
26
+ "Track 2 (Creative)",
27
+ ]
28
 
29
+
30
+ # DATA HANDLING FUNCTIONS
31
  def load_data(filename, repo_id):
 
32
  try:
33
+ filepath = hf_hub_download(
34
+ repo_id=repo_id, filename=filename, repo_type="dataset", token=HF_TOKEN
35
+ )
36
+ return pd.read_csv(filepath, dtype={"track": str, "category": str})
37
  except Exception:
38
  if filename == PROJECTS_FILE:
39
+ return pd.DataFrame(
40
+ columns=[
41
+ "space_url",
42
+ "video_url",
43
+ "track",
44
+ "category",
45
+ "submitted_by",
46
+ "timestamp",
47
+ ]
48
+ )
49
  elif filename == VOTES_FILE:
50
+ return pd.DataFrame(
51
+ columns=["space_url", "voted_by", "track", "category", "timestamp"]
52
+ )
53
  return pd.DataFrame()
54
 
55
+
56
  def save_data(df, filename, repo_id, commit_message):
 
57
  temp_path = f"./{filename}"
58
  df.to_csv(temp_path, index=False)
59
  upload_file(
60
+ path_or_fileobj=temp_path,
61
+ path_in_repo=filename,
62
+ repo_id=repo_id,
63
+ repo_type="dataset",
64
+ token=HF_TOKEN,
65
+ commit_message=commit_message,
66
  )
67
  os.remove(temp_path)
68
 
69
+
70
  # --- CORE LOGIC ---
71
 
72
+
73
  def get_username(request: gr.Request):
74
+ return (
75
+ request.request.session.get("oauth_info", {})
76
+ .get("userinfo", {})
77
+ .get("preferred_username")
78
+ )
79
+
80
 
81
  def parse_view_to_context(filter_view: str):
82
+ if "Track 1" in filter_view:
83
+ return TRACKS[0], "Overall"
84
  if "Track 2" in filter_view:
85
+ if "Enterprise" in filter_view:
86
+ return TRACKS[1], "Enterprise"
87
+ if "Consumer" in filter_view:
88
+ return TRACKS[1], "Consumer"
89
+ if "Creative" in filter_view:
90
+ return TRACKS[1], "Creative"
91
  return None, None
92
 
93
+
94
  def render_leaderboard(request: gr.Request, filter_view: str):
 
95
  username = get_username(request)
96
  projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
97
  votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
98
  vote_context_str = f"ℹ️ Your vote will be cast in the **'{filter_view}'** context."
99
 
100
  if projects_df.empty:
101
+ return (
102
+ "<h3>No projects submitted yet.</h3>",
103
+ gr.update(choices=[], value=None),
104
+ vote_context_str,
105
+ gr.update(visible=False),
106
+ )
107
 
108
  track_filter, category_filter = parse_view_to_context(filter_view)
109
  display_df = pd.DataFrame()
110
 
111
  if track_filter == TRACKS[0]:
112
+ track1_projects = projects_df[
113
+ projects_df["track"].str.contains(TRACKS[0], na=False)
114
+ ].copy()
115
  if not track1_projects.empty:
116
+ track1_projects["display_category"] = track1_projects[
117
+ "category"
118
+ ].str.replace(";", " | ")
119
+ track1_votes = votes_df[
120
+ (votes_df["track"] == TRACKS[0]) & (votes_df["category"] == "Overall")
121
+ ]
122
+ vote_counts = (
123
+ track1_votes.groupby("space_url").size().reset_index(name="votes")
124
+ )
125
+ display_df = pd.merge(
126
+ track1_projects, vote_counts, on="space_url", how="left"
127
+ ).fillna(0)
128
  elif track_filter == TRACKS[1]:
129
+ projects_df["track"] = projects_df["track"].str.split(";")
130
+ exploded_tracks = projects_df.explode("track")
131
+ exploded_tracks["category"] = exploded_tracks["category"].str.split(";")
132
+ exploded_projects = exploded_tracks.explode("category")
133
+ track2_filtered = exploded_projects[
134
+ (exploded_projects["track"] == track_filter)
135
+ & (exploded_projects["category"] == category_filter)
136
+ ]
137
  if not track2_filtered.empty:
138
+ context_votes = votes_df[
139
+ (votes_df["track"] == track_filter)
140
+ & (votes_df["category"] == category_filter)
141
+ ]
142
+ vote_counts = (
143
+ context_votes.groupby("space_url").size().reset_index(name="votes")
144
+ )
145
+ display_df = pd.merge(
146
+ track2_filtered, vote_counts, on="space_url", how="left"
147
+ ).fillna(0)
148
+ display_df["display_category"] = display_df["category"]
149
+
150
  if display_df.empty:
151
+ return (
152
+ f"<h3>No projects submitted for '{filter_view}' yet.</h3>",
153
+ gr.update(choices=[], value=None),
154
+ vote_context_str,
155
+ gr.update(visible=False),
156
+ )
157
+
158
+ display_df["votes"] = display_df["votes"].astype(int)
159
+ display_df = display_df.sort_values(by="votes", ascending=False).reset_index(
160
+ drop=True
161
+ )
162
 
163
+ display_df["rank"] = (
164
+ display_df["votes"].rank(method="dense", ascending=False).astype(int)
165
+ )
166
 
167
  html = "<div>"
168
+ trophies = {1: "πŸ₯‡", 2: "πŸ₯ˆ", 3: "πŸ₯‰"} # Use rank numbers 1, 2, 3 as keys
169
+ for _, row in display_df.iterrows():
170
+ rank_num = row["rank"]
171
+ rank_display = trophies.get(rank_num, f"<b>#{rank_num}</b>")
172
+ space_name = re.sub(r"https://huggingface.co/spaces/", "", row["space_url"])
173
  category_text = row["display_category"]
174
+ submitter_name = row["submitted_by"]
175
+
176
+ video_link_html = ""
177
+ if pd.notna(row["video_url"]) and str(row["video_url"]).strip():
178
+ video_link_html = (
179
+ f' | <a href="{row["video_url"]}" target="_blank">🎬 Video</a>'
180
+ )
181
+
182
  action_button_html = ""
183
  if username and row["submitted_by"] == username:
184
  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>'
185
 
186
  html += f"""
187
  <div style="display: flex; align-items: center; padding: 12px; border-bottom: 1px solid #eee; gap: 15px;" data-space-url="{row['space_url']}">
188
+ <div style="font-size: 24px; width: 50px;">{rank_display}</div>
189
  <div style="flex-grow: 1;">
190
+ <div style="font-weight: bold; font-size: 16px;">{space_name} <span style="font-weight: normal; color: #888; font-size: 14px;">({submitter_name})</span></div>
191
  <div style="font-size: 12px; color: #555;">
192
+ <a href="{row["space_url"]}" target="_blank">πŸš€ Space</a>
193
+ {video_link_html}
194
+ | <span style="color: #777;">Track: {row["track"].split(';')[0].split(':')[0]} | Categories: {category_text}</span>
195
  </div>
196
  </div>
197
  <div style="font-size: 20px; font-weight: bold; color: #3B82F6;">{row["votes"]} votes</div>
 
199
  </div>
200
  """
201
  html += "</div>"
202
+
203
+ project_urls = display_df["space_url"].unique().tolist()
204
+ return (
205
+ html,
206
+ gr.update(choices=project_urls, value=None),
207
+ vote_context_str,
208
+ gr.update(visible=False),
209
+ )
210
 
211
 
212
+ def submit_project(
213
+ request: gr.Request, space_url, video_url, selected_tracks, selected_categories
214
+ ):
215
  username = get_username(request)
216
  if not username:
217
  gr.Info("Error: You must be logged in to submit a project.")
218
  return gr.skip(), gr.skip(), gr.skip(), gr.skip()
219
+
220
+ if not all([space_url, selected_tracks, selected_categories]):
221
+ gr.Info(
222
+ "Error: Space URL, at least one Track, and at least one Category are required."
223
+ )
224
+ return gr.skip(), gr.skip(), gr.skip(), gr.skip()
225
+
226
+ if not "huggingface.co/spaces/" in space_url:
227
+ gr.Info("Error: Please enter a valid Hugging Face Space URL.")
228
  return gr.skip(), gr.skip(), gr.skip(), gr.skip()
229
+
230
  projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
231
+ if space_url in projects_df["space_url"].values:
232
  gr.Info("Error: This project has already been submitted.")
233
  return gr.skip(), gr.skip(), gr.skip(), gr.skip()
234
+
235
  tracks_str = ";".join(selected_tracks)
236
  categories_str = ";".join(selected_categories)
237
+ new_project = pd.DataFrame(
238
+ [
239
+ {
240
+ "space_url": space_url,
241
+ "video_url": video_url,
242
+ "track": tracks_str,
243
+ "category": categories_str,
244
+ "submitted_by": username,
245
+ "timestamp": datetime.now().isoformat(),
246
+ }
247
+ ]
248
+ )
249
  updated_projects = pd.concat([projects_df, new_project], ignore_index=True)
250
+ save_data(
251
+ updated_projects,
252
+ PROJECTS_FILE,
253
+ DATASET_REPO_ID,
254
+ f"Project submitted by {username}",
255
+ )
256
  gr.Info(f"βœ… Success! Project '{space_url.split('/')[-1]}' submitted.")
257
+ html, dropdown, context, edit_box_visibility = render_leaderboard(
258
+ request, LEADERBOARD_VIEWS[0]
259
+ )
260
  return f"Last action: Success.", html, dropdown, context
261
 
262
 
 
264
  username = get_username(request)
265
  if not username:
266
  gr.Info("Error: You must be logged in to vote.")
267
+ return gr.skip(), gr.skip(), gr.skip(), gr.skip() # Return 4 values
268
  if not project_to_vote:
269
  gr.Info("Error: Please select a project to vote for.")
270
+ return gr.skip(), gr.skip(), gr.skip(), gr.skip() # Return 4 values
271
+
272
  votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
273
  vote_track, vote_category = parse_view_to_context(filter_view)
274
+ existing_vote = votes_df[
275
+ (votes_df["space_url"] == project_to_vote)
276
+ & (votes_df["voted_by"] == username)
277
+ & (votes_df["track"] == vote_track)
278
+ & (votes_df["category"] == vote_category)
279
+ ]
280
  if not existing_vote.empty:
281
+ gr.Info(
282
+ f"Notice: You have already voted for this project in the '{filter_view}' context."
283
+ )
284
+ return gr.skip(), gr.skip(), gr.skip(), gr.skip() # Return 4 values
285
+
286
+ new_vote = pd.DataFrame(
287
+ [
288
+ {
289
+ "space_url": project_to_vote,
290
+ "voted_by": username,
291
+ "track": vote_track,
292
+ "category": vote_category,
293
+ "timestamp": datetime.now().isoformat(),
294
+ }
295
+ ]
296
+ )
297
  updated_votes = pd.concat([votes_df, new_vote], ignore_index=True)
298
  save_data(updated_votes, VOTES_FILE, DATASET_REPO_ID, f"Vote cast by {username}")
299
+ gr.Info(
300
+ f"βœ… Vote successfully cast for '{project_to_vote.split('/')[-1]}' in '{filter_view}'!"
301
+ )
302
+
303
+ html, dropdown, context, _ = render_leaderboard(request, filter_view)
304
+
305
+ return f"Last action: Success.", html, dropdown, context
306
 
307
 
308
+ def edit_project(
309
+ request: gr.Request,
310
+ old_space_url: str,
311
+ new_space_url: str,
312
+ new_video_url: str,
313
+ filter_view: str,
314
+ ):
315
  username = get_username(request)
316
  if not username:
317
  gr.Info("Error: Authentication failed. Please log in again.")
318
  return gr.skip(), gr.skip(), gr.skip(), gr.update(visible=False)
319
  projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
320
  votes_df = load_data(VOTES_FILE, DATASET_REPO_ID)
321
+ project_index = projects_df[
322
+ (projects_df["space_url"] == old_space_url)
323
+ & (projects_df["submitted_by"] == username)
324
+ ].index
325
  if project_index.empty:
326
+ gr.Info(
327
+ "Error: You do not have permission to edit this project, or it no longer exists."
328
+ )
329
  return gr.skip(), gr.skip(), gr.skip(), gr.update(visible=False)
330
+ projects_df.loc[project_index, "space_url"] = new_space_url
331
+ projects_df.loc[project_index, "video_url"] = new_video_url
332
+ save_data(
333
+ projects_df,
334
+ PROJECTS_FILE,
335
+ DATASET_REPO_ID,
336
+ f"Project edited by {username}: {old_space_url} -> {new_space_url}",
337
+ )
338
  if old_space_url != new_space_url:
339
+ votes_df.loc[votes_df["space_url"] == old_space_url, "space_url"] = (
340
+ new_space_url
341
+ )
342
+ save_data(
343
+ votes_df,
344
+ VOTES_FILE,
345
+ DATASET_REPO_ID,
346
+ f"Vote URLs updated for project edit by {username}",
347
+ )
348
  gr.Info("βœ… Project URLs updated successfully! Votes have been preserved.")
349
  html, dropdown, context, _ = render_leaderboard(request, filter_view)
350
  return "Project edited.", html, dropdown, context, gr.update(visible=False)
351
 
352
+
353
  # --- THEME ---
354
+ theme = gr.themes.Default(
355
+ primary_hue="blue", secondary_hue="yellow", neutral_hue="neutral"
356
+ ).set(
357
+ body_background_fill="*neutral_100",
358
+ body_background_fill_dark="*neutral_800",
359
+ body_text_color="*neutral_700",
360
+ body_text_color_dark="*neutral_200",
361
+ body_text_size="1.1em",
362
+ code_background_fill="*neutral_100",
363
+ code_background_fill_dark="*neutral_800",
364
+ shadow_drop="2px 2px 4px *neutral_400",
365
+ block_label_background_fill="*neutral_100",
366
+ block_label_background_fill_dark="*neutral_800",
367
+ block_label_text_color="*neutral_700",
368
+ block_label_text_color_dark="*neutral_200",
369
+ block_title_text_color="*primary_700",
370
+ block_title_text_color_dark="*primary_300",
371
+ panel_background_fill="*neutral_50",
372
+ panel_background_fill_dark="*neutral_900",
373
+ panel_border_color="*neutral_200",
374
+ panel_border_color_dark="*neutral_700",
375
+ checkbox_border_color="*neutral_300",
376
+ checkbox_border_color_dark="*neutral_600",
377
+ input_background_fill="white",
378
+ input_background_fill_dark="*neutral_800",
379
+ input_border_color="*neutral_300",
380
+ input_border_color_dark="*neutral_600",
381
+ slider_color="*primary_500",
382
+ slider_color_dark="*primary_400",
383
+ button_primary_background_fill="*primary_600",
384
+ button_primary_background_fill_dark="*primary_500",
385
+ button_primary_background_fill_hover="*primary_700",
386
+ button_primary_background_fill_hover_dark="*primary_600",
387
+ button_primary_text_color="white",
388
+ button_primary_text_color_dark="white",
389
+ button_secondary_background_fill="*secondary_400",
390
+ button_secondary_background_fill_dark="*secondary_500",
391
+ button_secondary_background_fill_hover="*secondary_500",
392
+ button_secondary_background_fill_hover_dark="*secondary_600",
393
+ button_secondary_text_color="*neutral_700",
394
+ button_secondary_text_color_dark="*neutral_200",
395
+ button_cancel_background_fill="*neutral_200",
396
+ button_cancel_background_fill_dark="*neutral_700",
397
+ button_cancel_background_fill_hover="*neutral_300",
398
+ button_cancel_background_fill_hover_dark="*neutral_600",
399
+ button_cancel_text_color="*neutral_700",
400
+ button_cancel_text_color_dark="*neutral_200",
401
  )
402
 
403
  # --- GRADIO INTERFACE ---
404
  with gr.Blocks(theme=theme, title="Hackathon Community Choice") as app:
405
  gr.Markdown("# πŸ† Hackathon Community Choice Award")
406
+ gr.Markdown(
407
+ "Vote for your favorite hackathon projects! Please log in to participate."
408
+ )
409
  gr.LoginButton()
410
 
411
  with gr.Tabs() as tabs:
412
  with gr.TabItem("πŸ† Leaderboard & Vote", id=0):
413
+ leaderboard_filter = gr.Radio(
414
+ LEADERBOARD_VIEWS,
415
+ label="Select Leaderboard View",
416
+ value=LEADERBOARD_VIEWS[0],
417
+ )
418
  with gr.Group(visible=False) as edit_group:
419
  gr.Markdown("### Edit Project URLs")
420
  edit_old_url = gr.Textbox(label="Original Space URL", interactive=False)
 
423
  with gr.Row():
424
  save_edit_button = gr.Button("Save Changes", variant="primary")
425
  cancel_edit_button = gr.Button("Cancel")
426
+
427
  with gr.Row():
428
  with gr.Column(scale=2):
429
  leaderboard_html = HTMLPlus(
430
+ "Please log in to load...", selectable_elements=[".edit-button"]
 
431
  )
432
  with gr.Column(scale=1):
433
  gr.Markdown("### Cast Your Vote")
434
  vote_context_display = gr.Markdown()
435
  vote_status = gr.Markdown()
436
+ project_dropdown = gr.Dropdown(
437
+ label="Select a Project to Vote For", interactive=True
438
+ )
439
+ vote_button = gr.Button(
440
+ "πŸ‘ Vote for Selected Project", variant="primary"
441
+ )
442
+
443
  with gr.TabItem("πŸš€ Submit Your Project", id=1):
444
  gr.Markdown("### Register Your Project")
445
  submission_status = gr.Markdown()
446
+ space_url_input = gr.Textbox(
447
+ label="Your Hugging Face Space URL",
448
+ placeholder="https://huggingface.co/spaces/...",
449
+ )
450
+ # NEW: Video URL now has "(Optional)" in the label
451
+ video_url_input = gr.Textbox(
452
+ label="Your Demo Video URL (Optional)",
453
+ placeholder="https://www.youtube.com/watch?v=...",
454
+ )
455
  track_checkboxes = gr.CheckboxGroup(TRACKS, label="Select Your Track(s)")
456
+ category_checkboxes = gr.CheckboxGroup(
457
+ CATEGORIES, label="Select Your Category(s)"
458
+ )
459
  submit_button = gr.Button("Submit Project", variant="primary")
460
 
461
  # --- Event Handling ---
 
470
 
471
  def handle_leaderboard_click(evt: gr.SelectData):
472
  if evt.index == ".edit-button":
473
+ project_to_edit_url = evt.value.get("data-url")
474
  if project_to_edit_url:
475
  projects_df = load_data(PROJECTS_FILE, DATASET_REPO_ID)
476
+ project_data = projects_df[
477
+ projects_df["space_url"] == project_to_edit_url
478
+ ].iloc[0]
479
+ return (
480
+ gr.update(visible=True),
481
+ project_data["space_url"],
482
+ project_data["space_url"],
483
+ project_data["video_url"],
484
+ )
485
  return gr.skip(), gr.skip(), gr.skip(), gr.skip()
486
 
487
+ app.load(
488
+ fn=handle_page_load,
489
+ inputs=[leaderboard_filter],
490
+ outputs=[leaderboard_html, project_dropdown, vote_context_display, edit_group],
491
+ )
492
+ leaderboard_filter.change(
493
+ fn=render_leaderboard,
494
+ inputs=[leaderboard_filter],
495
+ outputs=[leaderboard_html, project_dropdown, vote_context_display, edit_group],
496
+ )
497
+ submit_button.click(
498
+ fn=submit_project,
499
+ inputs=[
500
+ space_url_input,
501
+ video_url_input,
502
+ track_checkboxes,
503
+ category_checkboxes,
504
+ ],
505
+ outputs=[
506
+ submission_status,
507
+ leaderboard_html,
508
+ project_dropdown,
509
+ vote_context_display,
510
+ ],
511
+ )
512
+ vote_button.click(
513
+ fn=cast_vote,
514
+ inputs=[project_dropdown, leaderboard_filter],
515
+ outputs=[vote_status, leaderboard_html, project_dropdown, vote_context_display],
516
+ )
517
+
518
+ leaderboard_html.select(
519
+ fn=handle_leaderboard_click,
520
+ outputs=[edit_group, edit_old_url, edit_new_url, edit_new_video_url],
521
+ )
522
+
523
+ save_edit_button.click(
524
+ fn=edit_project,
525
+ inputs=[edit_old_url, edit_new_url, edit_new_video_url, leaderboard_filter],
526
+ outputs=[
527
+ submission_status,
528
+ leaderboard_html,
529
+ project_dropdown,
530
+ vote_context_display,
531
+ edit_group,
532
+ ],
533
+ )
534
+ cancel_edit_button.click(
535
+ fn=cancel_edit,
536
+ outputs=[edit_group, edit_old_url, edit_new_url, edit_new_video_url],
537
+ )
538
+
539
+ app.launch()