feature/verify_email

#2
by EmmaScharfmann HF Staff - opened
Dockerfile ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10-slim
2
+
3
+ RUN apt-get update && apt-get install -y \
4
+ build-essential \
5
+ cmake \
6
+ libnetcdf-dev \
7
+ && rm -rf /var/lib/apt/lists/*
8
+
9
+ RUN useradd -m -u 1000 user
10
+ USER user
11
+
12
+ ENV HOME=/home/user \
13
+ PATH=/home/user/.local/bin:$PATH
14
+
15
+ WORKDIR $HOME/app
16
+
17
+ RUN pip install --no-cache-dir --upgrade pip
18
+ COPY --chown=user . $HOME/app
19
+ RUN pip install -r requirements.txt
20
+
21
+ EXPOSE 7860
22
+ ENV GRADIO_SERVER_NAME="0.0.0.0"
23
+
24
+ CMD ["python", "app.py"]
README.md CHANGED
@@ -3,13 +3,15 @@ title: MecCog Challenge
3
  emoji: 🔋
4
  colorFrom: purple
5
  colorTo: green
6
- sdk: gradio
7
- sdk_version: "5.50.0"
8
  pinned: false
9
  hf_oauth: true
10
  hf_oauth_expiration_minutes: 480
11
  hf_oauth_scopes:
12
- - email
 
 
 
13
  ---
14
 
15
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
3
  emoji: 🔋
4
  colorFrom: purple
5
  colorTo: green
6
+ sdk: docker
 
7
  pinned: false
8
  hf_oauth: true
9
  hf_oauth_expiration_minutes: 480
10
  hf_oauth_scopes:
11
+ - read-repos
12
+ - write-repos
13
+ - manage-repos
14
+ - inference-api
15
  ---
16
 
17
  Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
about.py CHANGED
@@ -5,12 +5,12 @@ from huggingface_hub import HfApi
5
  load_dotenv()
6
 
7
  TOKEN = os.environ.get("HF_TOKEN")
8
- CACHE_PATH = os.getenv("HF_HOME", ".")
9
  API = HfApi(token=TOKEN)
10
 
11
- CHALLENGE_NAME = "MecCogChallenge"
12
- _ORGANIZATION = "MecCog"
13
 
14
- SUBMISSIONS_REPO = f"{_ORGANIZATION}/{CHALLENGE_NAME}"
15
- RESULTS_REPO = f"{_ORGANIZATION}/{CHALLENGE_NAME}"
16
- REGISTRATION_REPO = f"{_ORGANIZATION}/{CHALLENGE_NAME}"
 
5
  load_dotenv()
6
 
7
  TOKEN = os.environ.get("HF_TOKEN")
8
+ CACHE_PATH=os.getenv("HF_HOME", ".")
9
  API = HfApi(token=TOKEN)
10
 
11
+ CHALLENGE_NAME="MecCogChallenge"
12
+ _ORGANIZATION="MecCog"
13
 
14
+ SUBMISSIONS_REPO = f'{_ORGANIZATION}/{CHALLENGE_NAME}Submissions'
15
+ RESULTS_REPO = f'{_ORGANIZATION}/{CHALLENGE_NAME}Results'
16
+ REGISTRATION_REPO = f'{_ORGANIZATION}/{CHALLENGE_NAME}Registrations'
app.py CHANGED
@@ -7,29 +7,16 @@ from about import CHALLENGE_NAME
7
 
8
  PROBLEM_TYPES = ["hypothesis 1", "hypothesis 2", "hypothesis 3"]
9
 
10
-
11
  def gradio_interface() -> gr.Blocks:
12
  with gr.Blocks() as demo:
13
  gr.Markdown(f"## Welcome to the {CHALLENGE_NAME}!")
14
-
15
- active_tab = gr.BrowserState(0)
16
-
17
- with gr.Tabs(elem_classes="tab-buttons") as tabs:
18
  challenge_page.get_description(gr=gr)
19
- registration_page.get_registration_page(gr=gr, demo=demo)
20
- submission_page.get_submission_page(gr=gr, demo=demo)
21
  faq.get_faq(gr=gr)
22
  leaderboard_page.get_leaderboard(gr=gr)
23
-
24
- def restore_tab(idx):
25
- return gr.Tabs(selected=idx)
26
-
27
- def save_tab(evt: gr.SelectData):
28
- return evt.index
29
-
30
- tabs.select(fn=save_tab, inputs=None, outputs=[active_tab])
31
- demo.load(fn=restore_tab, inputs=[active_tab], outputs=[tabs])
32
-
33
  return demo
34
 
35
 
 
7
 
8
  PROBLEM_TYPES = ["hypothesis 1", "hypothesis 2", "hypothesis 3"]
9
 
 
10
  def gradio_interface() -> gr.Blocks:
11
  with gr.Blocks() as demo:
12
  gr.Markdown(f"## Welcome to the {CHALLENGE_NAME}!")
13
+ with gr.Tabs(elem_classes="tab-buttons"):
 
 
 
14
  challenge_page.get_description(gr=gr)
15
+ registration_page.get_registration_page(gr=gr)
16
+ submission_page.get_submission_page(gr=gr)
17
  faq.get_faq(gr=gr)
18
  leaderboard_page.get_leaderboard(gr=gr)
19
+
 
 
 
 
 
 
 
 
 
20
  return demo
21
 
22
 
components/challenge_page.py CHANGED
@@ -16,5 +16,5 @@ CHALLENGE_DESCRIPTION = f"""
16
 
17
 
18
  def get_description(gr):
19
- with gr.TabItem(TAB_TITLE, elem_id="boundary-benchmark-tab-table", id=0):
20
- gr.Markdown(CHALLENGE_DESCRIPTION)
 
16
 
17
 
18
  def get_description(gr):
19
+ with gr.TabItem(TAB_TITLE, elem_id="boundary-benchmark-tab-table"):
20
+ gr.Markdown(CHALLENGE_DESCRIPTION)
components/registration/config.py CHANGED
@@ -38,15 +38,13 @@ TEAMS_FILE_NAME = "teams.csv"
38
  # Schema – column names and dtypes for each dataset
39
  # ---------------------------------------------------------------------------
40
  PARTICIPANT_COLUMNS: list[str] = [
41
- "username",
42
  "email",
43
  "name",
44
  "affiliation",
45
  "discord",
46
  "self_description",
47
- "registration_status",
48
- "team_name",
49
- "is_leader",
50
  "registered_at",
51
  "updated_at",
52
  ]
@@ -54,9 +52,9 @@ PARTICIPANT_COLUMNS: list[str] = [
54
  TEAM_COLUMNS: list[str] = [
55
  "team_name",
56
  "team_description",
57
- "leader_username",
58
- "members_username", # JSON-encoded list[str]
59
  "second_team_reason",
60
  "created_at",
61
  "updated_at",
62
- ]
 
38
  # Schema – column names and dtypes for each dataset
39
  # ---------------------------------------------------------------------------
40
  PARTICIPANT_COLUMNS: list[str] = [
 
41
  "email",
42
  "name",
43
  "affiliation",
44
  "discord",
45
  "self_description",
46
+ "needs_manual_review",
47
+ "team_memberships",
 
48
  "registered_at",
49
  "updated_at",
50
  ]
 
52
  TEAM_COLUMNS: list[str] = [
53
  "team_name",
54
  "team_description",
55
+ "leader_email",
56
+ "member_emails", # JSON-encoded list[str]
57
  "second_team_reason",
58
  "created_at",
59
  "updated_at",
60
+ ]
components/registration/registration_page.py CHANGED
@@ -4,7 +4,6 @@ import json
4
  import logging
5
  from datetime import datetime, timezone
6
 
7
- import gradio
8
  import pandas as pd
9
 
10
  from about import REGISTRATION_REPO
@@ -17,18 +16,13 @@ from components.registration.config import (
17
  TEAM_COLUMNS,
18
  )
19
  from components.registration.utils import (
 
20
  validate_email_domain,
21
  is_institutional_email,
22
  validate_email_format,
23
  get_user_teams,
24
- rename_user_teams,
25
- )
26
- from components.utils import (
27
- push_data_to_dataset,
28
- load_data_from_dataset,
29
- get_team,
30
- prefill_user_info,
31
  )
 
32
 
33
  logger = logging.getLogger(__name__)
34
 
@@ -38,12 +32,10 @@ def _validate_registration_input(data: dict[str, str]) -> list[str]:
38
  errors: list[str] = []
39
  email = (data.get("email") or "").strip()
40
  name = (data.get("name") or "").strip()
41
- username = (data.get("username") or "").strip()
42
  affiliation = (data.get("affiliation") or "").strip()
43
  role = (data.get("role") or "").strip()
44
 
45
- if not name or not username:
46
- return ["❌ You must be logged in with your HuggingFace account to register."]
47
  if not email:
48
  errors.append("An email address is required.")
49
  elif not validate_email_format(email):
@@ -87,7 +79,6 @@ def register_participant(data: dict) -> tuple[bool, str]:
87
  Persist a participant registration to HuggingFace datasets.
88
 
89
  ``data`` keys (all str unless noted):
90
- username – always required, unique from huggingface account
91
  email – always required
92
  name – always required
93
  affiliation – always required
@@ -107,19 +98,18 @@ def register_participant(data: dict) -> tuple[bool, str]:
107
  if errors:
108
  return False, "❌ Validation failed:\n" + "\n".join(f" • {e}" for e in errors)
109
 
110
- username = data["username"].strip() # unique
111
-
112
- email = data["email"].strip()
113
- name = data["name"].strip()
114
- affiliation = data["affiliation"].strip()
115
- role = data["role"].strip()
116
  self_description = (data.get("self_description") or "").strip()
117
  is_new_team: bool = bool(data.get("is_new_team", False))
118
- team_name = data["team_name"].strip()
119
  team_description = (data.get("team_description") or "").strip()
120
  is_leader: bool = bool(data.get("is_leader", False))
121
  second_team_reason = (data.get("second_team_reason") or "").strip()
122
  institutional = is_institutional_email(email=email)
 
123
  now = datetime.now(timezone.utc).isoformat()
124
 
125
  # ── 2. Load both datasets ────────────────────────────────────────────────
@@ -143,42 +133,34 @@ def register_participant(data: dict) -> tuple[bool, str]:
143
  "❌ This team already exists. Please provide another Team name, or join an existing team. .",
144
  )
145
 
146
- # ── 3. Resolve participant row (upsert by username + team_name) ──────────
147
- existing_participant = participants_df[participants_df["username"] == username]
148
  is_new_participant = existing_participant.empty
149
 
150
- existing_in_this_team = existing_participant[
151
- existing_participant["team_name"] == team_name # fix: was "team"
152
- ]
153
- is_already_in_this_team = not existing_in_this_team.empty
154
-
155
- is_second_team = not existing_participant.empty and not is_already_in_this_team
156
-
157
- registration_status = (
158
- "Pending" if not institutional or is_second_team else "Validated"
159
- )
160
- if is_already_in_this_team:
161
- registration_status = existing_in_this_team.iloc[0].get(
162
- "registration_status", "Pending"
163
- )
164
-
165
  if is_new_participant:
 
166
  registered_at = now
167
  else:
 
 
 
168
  registered_at = existing_participant.iloc[0].get("registered_at", now)
169
- if is_second_team and not second_team_reason:
170
- return (
171
- False,
172
- "This user is already part of a team. Please provide a reason for joining a second team.",
173
- )
 
 
 
174
  # ── 4. Resolve team ──────────────────────────────────────────────────────
175
  if is_new_team:
176
  # ── 4a. Creating a new team ──────────────────────────────────────────
177
  new_team_row = {
178
  "team_name": team_name,
179
  "team_description": team_description,
180
- "leader_username": username if is_leader else "",
181
- "members_username": json.dumps([username]),
182
  "second_team_reason": second_team_reason,
183
  "created_at": now,
184
  "updated_at": now,
@@ -199,59 +181,68 @@ def register_participant(data: dict) -> tuple[bool, str]:
199
  )
200
 
201
  # Add participant to member list if not already there
202
- members_username: list[str] = json.loads(
203
- team_row.get("members_username") or "[]"
204
- )
205
- if username not in members_username:
206
- members_username.append(username)
207
 
208
  # Assign leader if requested and slot is free
209
- current_leader = (team_row.get("leader_username") or "").strip()
210
  if is_leader:
211
- if current_leader and current_leader != username:
212
  return False, (
213
  f"❌ Team '{name}' already has a designated leader "
214
  f"({current_leader}). "
215
  "Please contact the current leader to transfer leadership."
216
  )
217
- new_leader = username
218
  else:
219
  new_leader = current_leader # unchanged
220
 
221
  # Update team row in-place
222
  mask = teams_df["team_name"] == team_name
223
- teams_df.loc[mask, "members_username"] = json.dumps(members_username)
224
- teams_df.loc[mask, "leader_username"] = new_leader
225
  teams_df.loc[mask, "updated_at"] = now
226
  action_msg = f"Joined existing team '{team_name}' ({team_name})."
227
 
228
- # ── 5. Upsert participant row (keyed on username + team_name) ────────────
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
229
  participant_row = {
230
  "email": email,
231
  "name": name,
232
- "username": username,
233
  "affiliation": affiliation,
234
  "role": role,
235
  "self_description": self_description,
236
- "registration_status": registration_status,
237
- "team_name": team_name,
238
- "is_leader": is_leader,
239
  "registered_at": registered_at,
240
  "updated_at": now,
241
  }
242
 
243
- if is_already_in_this_team:
244
- mask = (participants_df["username"] == username) & (
245
- participants_df["team_name"] == team_name
246
- )
247
- for col, val in participant_row.items():
248
- participants_df.loc[mask, col] = val
249
- else:
250
  participants_df = pd.concat(
251
  [participants_df, pd.DataFrame([participant_row])], ignore_index=True
252
  )
 
 
 
253
 
254
- # ── 6. Push both datasets back to HF ────────────────────────────────────
255
  try:
256
  push_data_to_dataset(participants_df, REGISTRATION_REPO, PARTICIPANTS_FILE_NAME)
257
  push_data_to_dataset(teams_df, REGISTRATION_REPO, TEAMS_FILE_NAME)
@@ -267,38 +258,31 @@ def register_participant(data: dict) -> tuple[bool, str]:
267
  review_note = (
268
  "Your registration requires manual review because no institutional email "
269
  "was provided – you may receive a follow-up email."
270
- if registration_status == "Pending"
271
  else ""
272
  )
273
  message = (
274
- f"Registration {'created' if is_new_participant else 'updated'} for {username}. "
275
- f"{action_msg} {leader_note} {review_note}"
276
  )
277
  return True, message
278
 
279
 
280
- def get_registration_page(gr, demo):
281
- with gr.TabItem("Register", id=1):
282
  gr.Markdown(REGISTRATION_DESCRIPTION)
283
 
284
- gr.LoginButton()
285
-
286
  with gr.Row():
287
  with gr.Column():
288
- username = gr.Textbox(
289
- label="Username from your Hugging Face account *",
290
- placeholder="Fetching from HuggingFace...",
291
- interactive=False,
292
  )
293
  name = gr.Textbox(
294
- label="Full name from your Hugging Face account *",
295
- placeholder="Fetching from HuggingFace...",
296
- interactive=False,
297
  )
298
- email = gr.Textbox(
299
- label="Email *", placeholder="myemail@domain.com", interactive=True
300
- )
301
-
302
  affiliation = gr.Textbox(
303
  label="Affiliation / Institution *",
304
  placeholder="University / Company name",
@@ -343,14 +327,13 @@ def get_registration_page(gr, demo):
343
  visible=False,
344
  )
345
 
346
- def on_see_user_teams(hf_username: str):
347
- if not hf_username or not hf_username.strip():
348
  return gr.Dataframe(visible=False), gr.Markdown(
349
- "❌ You must be logged in with your HuggingFace account to see your teams.",
350
- visible=True,
351
  )
352
 
353
- teams = rename_user_teams(get_user_teams(username=hf_username))
354
 
355
  if len(teams) > 0:
356
  return gr.Dataframe(value=teams, visible=True), gr.Markdown(
@@ -363,7 +346,7 @@ def get_registration_page(gr, demo):
363
 
364
  user_team_button.click(
365
  fn=on_see_user_teams,
366
- inputs=[username],
367
  outputs=[user_teams, user_teams_empty_msg],
368
  )
369
 
@@ -409,6 +392,7 @@ def get_registration_page(gr, demo):
409
 
410
  def on_register(
411
  registration_email: str,
 
412
  registration_affiliation: str,
413
  self_desc: str,
414
  is_new_team: bool,
@@ -417,19 +401,15 @@ def get_registration_page(gr, demo):
417
  role: str,
418
  is_leader: bool,
419
  second_reason: str,
420
- oauth_profile: gradio.OAuthProfile | None,
421
  ):
422
- if oauth_profile is None:
423
- return gr.update(
424
- value="❌ You must be logged in with your HuggingFace account to register.",
425
- visible=True,
426
- )
427
-
428
- errors: list[str] = []
429
 
430
  if not registration_email.strip():
431
  errors.append("Email is required.")
432
 
 
 
 
433
  if not registration_affiliation.strip():
434
  errors.append("Affiliation is required.")
435
  if registration_email.strip() and not validate_email_domain(
@@ -453,9 +433,8 @@ def get_registration_page(gr, demo):
453
  return gr.update(value=msg, visible=True)
454
 
455
  data = {
456
- "username": oauth_profile.username,
457
- "name": oauth_profile.name,
458
  "email": registration_email,
 
459
  "affiliation": registration_affiliation,
460
  "self_description": self_desc,
461
  "is_new_team": is_new_team,
@@ -478,6 +457,7 @@ def get_registration_page(gr, demo):
478
  fn=on_register,
479
  inputs=[
480
  email,
 
481
  affiliation,
482
  description,
483
  is_new_team_checkbox,
@@ -489,9 +469,3 @@ def get_registration_page(gr, demo):
489
  ],
490
  outputs=[registration_status],
491
  )
492
-
493
- demo.load(
494
- fn=prefill_user_info,
495
- inputs=[],
496
- outputs=[username, name, email],
497
- )
 
4
  import logging
5
  from datetime import datetime, timezone
6
 
 
7
  import pandas as pd
8
 
9
  from about import REGISTRATION_REPO
 
16
  TEAM_COLUMNS,
17
  )
18
  from components.registration.utils import (
19
+ get_team_membership_entry,
20
  validate_email_domain,
21
  is_institutional_email,
22
  validate_email_format,
23
  get_user_teams,
 
 
 
 
 
 
 
24
  )
25
+ from components.utils import push_data_to_dataset, load_data_from_dataset, get_team
26
 
27
  logger = logging.getLogger(__name__)
28
 
 
32
  errors: list[str] = []
33
  email = (data.get("email") or "").strip()
34
  name = (data.get("name") or "").strip()
 
35
  affiliation = (data.get("affiliation") or "").strip()
36
  role = (data.get("role") or "").strip()
37
 
38
+ # Email is always required
 
39
  if not email:
40
  errors.append("An email address is required.")
41
  elif not validate_email_format(email):
 
79
  Persist a participant registration to HuggingFace datasets.
80
 
81
  ``data`` keys (all str unless noted):
 
82
  email – always required
83
  name – always required
84
  affiliation – always required
 
98
  if errors:
99
  return False, "❌ Validation failed:\n" + "\n".join(f" • {e}" for e in errors)
100
 
101
+ email = data["email"].strip().lower()
102
+ name = data["name"].strip().lower()
103
+ affiliation = data["affiliation"].strip().lower()
104
+ role = data["role"].strip().lower()
 
 
105
  self_description = (data.get("self_description") or "").strip()
106
  is_new_team: bool = bool(data.get("is_new_team", False))
107
+ team_name = data["team_name"].strip().lower()
108
  team_description = (data.get("team_description") or "").strip()
109
  is_leader: bool = bool(data.get("is_leader", False))
110
  second_team_reason = (data.get("second_team_reason") or "").strip()
111
  institutional = is_institutional_email(email=email)
112
+ needs_manual_review = not institutional
113
  now = datetime.now(timezone.utc).isoformat()
114
 
115
  # ── 2. Load both datasets ────────────────────────────────────────────────
 
133
  "❌ This team already exists. Please provide another Team name, or join an existing team. .",
134
  )
135
 
136
+ # ── 3. Resolve participant row (upsert) ──────────────────────────────────
137
+ existing_participant = participants_df[participants_df["email"] == email]
138
  is_new_participant = existing_participant.empty
139
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
140
  if is_new_participant:
141
+ existing_memberships: list[dict] = []
142
  registered_at = now
143
  else:
144
+ existing_memberships = json.loads(
145
+ existing_participant.iloc[0].get("team_memberships") or "[]"
146
+ )
147
  registered_at = existing_participant.iloc[0].get("registered_at", now)
148
+
149
+ already_has_team = len(existing_memberships) > 0
150
+ if already_has_team and not second_team_reason:
151
+ return (
152
+ False,
153
+ "❌ This user is already part of a team. Please provide a reason for joining a second team.",
154
+ )
155
+
156
  # ── 4. Resolve team ──────────────────────────────────────────────────────
157
  if is_new_team:
158
  # ── 4a. Creating a new team ──────────────────────────────────────────
159
  new_team_row = {
160
  "team_name": team_name,
161
  "team_description": team_description,
162
+ "leader_email": email if is_leader else "",
163
+ "member_emails": json.dumps([email]),
164
  "second_team_reason": second_team_reason,
165
  "created_at": now,
166
  "updated_at": now,
 
181
  )
182
 
183
  # Add participant to member list if not already there
184
+ member_emails: list[str] = json.loads(team_row.get("member_emails") or "[]")
185
+ if email not in member_emails:
186
+ member_emails.append(email)
 
 
187
 
188
  # Assign leader if requested and slot is free
189
+ current_leader = (team_row.get("leader_email") or "").strip()
190
  if is_leader:
191
+ if current_leader and current_leader != email:
192
  return False, (
193
  f"❌ Team '{name}' already has a designated leader "
194
  f"({current_leader}). "
195
  "Please contact the current leader to transfer leadership."
196
  )
197
+ new_leader = email
198
  else:
199
  new_leader = current_leader # unchanged
200
 
201
  # Update team row in-place
202
  mask = teams_df["team_name"] == team_name
203
+ teams_df.loc[mask, "member_emails"] = json.dumps(member_emails)
204
+ teams_df.loc[mask, "leader_email"] = new_leader
205
  teams_df.loc[mask, "updated_at"] = now
206
  action_msg = f"Joined existing team '{team_name}' ({team_name})."
207
 
208
+ # ── 5. Build updated membership list for participant ─────────────────────
209
+ # If participant is already a member of this team, update is_leader in place
210
+ existing_team_names = [m["team_name"] for m in existing_memberships]
211
+ if team_name in existing_team_names:
212
+ updated_memberships = [
213
+ get_team_membership_entry(
214
+ team_name=m["team_name"],
215
+ is_leader=is_leader if m["team_name"] == team_name else m["is_leader"],
216
+ )
217
+ for m in existing_memberships
218
+ ]
219
+ else:
220
+ updated_memberships = existing_memberships + [
221
+ get_team_membership_entry(team_name=team_name, is_leader=is_leader)
222
+ ]
223
+
224
+ # ── 6. Upsert participant row ────────────────────────────────────────────
225
  participant_row = {
226
  "email": email,
227
  "name": name,
 
228
  "affiliation": affiliation,
229
  "role": role,
230
  "self_description": self_description,
231
+ "needs_manual_review": needs_manual_review,
232
+ "team_memberships": json.dumps(updated_memberships),
 
233
  "registered_at": registered_at,
234
  "updated_at": now,
235
  }
236
 
237
+ if is_new_participant:
 
 
 
 
 
 
238
  participants_df = pd.concat(
239
  [participants_df, pd.DataFrame([participant_row])], ignore_index=True
240
  )
241
+ else:
242
+ for col, val in participant_row.items():
243
+ participants_df.loc[participants_df["email"] == email, col] = val
244
 
245
+ # ── 7. Push both datasets back to HF ────────────────────────────────────
246
  try:
247
  push_data_to_dataset(participants_df, REGISTRATION_REPO, PARTICIPANTS_FILE_NAME)
248
  push_data_to_dataset(teams_df, REGISTRATION_REPO, TEAMS_FILE_NAME)
 
258
  review_note = (
259
  "Your registration requires manual review because no institutional email "
260
  "was provided – you may receive a follow-up email."
261
+ if needs_manual_review
262
  else ""
263
  )
264
  message = (
265
+ f"Registration {'created' if is_new_participant else 'updated'} for {email}. "
266
+ f"{action_msg}{leader_note}{review_note}"
267
  )
268
  return True, message
269
 
270
 
271
+ def get_registration_page(gr):
272
+ with gr.TabItem("Register"):
273
  gr.Markdown(REGISTRATION_DESCRIPTION)
274
 
 
 
275
  with gr.Row():
276
  with gr.Column():
277
+ email = gr.Textbox(
278
+ label="Institutional / Company Email *",
279
+ placeholder="you@university.edu or you@company.com",
280
+ info="Academic (.edu, .ac.*) or company domains are accepted automatically.",
281
  )
282
  name = gr.Textbox(
283
+ label="Full Name *",
284
+ placeholder="Jane Smith",
 
285
  )
 
 
 
 
286
  affiliation = gr.Textbox(
287
  label="Affiliation / Institution *",
288
  placeholder="University / Company name",
 
327
  visible=False,
328
  )
329
 
330
+ def on_see_user_teams(user_email: str):
331
+ if not user_email or not user_email.strip():
332
  return gr.Dataframe(visible=False), gr.Markdown(
333
+ "❌ Please enter an email address.", visible=True
 
334
  )
335
 
336
+ teams = get_user_teams(email=user_email)
337
 
338
  if len(teams) > 0:
339
  return gr.Dataframe(value=teams, visible=True), gr.Markdown(
 
346
 
347
  user_team_button.click(
348
  fn=on_see_user_teams,
349
+ inputs=[email],
350
  outputs=[user_teams, user_teams_empty_msg],
351
  )
352
 
 
392
 
393
  def on_register(
394
  registration_email: str,
395
+ registration_name: str,
396
  registration_affiliation: str,
397
  self_desc: str,
398
  is_new_team: bool,
 
401
  role: str,
402
  is_leader: bool,
403
  second_reason: str,
 
404
  ):
405
+ errors = []
 
 
 
 
 
 
406
 
407
  if not registration_email.strip():
408
  errors.append("Email is required.")
409
 
410
+ if not registration_name.strip():
411
+ errors.append("Full name is required.")
412
+
413
  if not registration_affiliation.strip():
414
  errors.append("Affiliation is required.")
415
  if registration_email.strip() and not validate_email_domain(
 
433
  return gr.update(value=msg, visible=True)
434
 
435
  data = {
 
 
436
  "email": registration_email,
437
+ "name": registration_name,
438
  "affiliation": registration_affiliation,
439
  "self_description": self_desc,
440
  "is_new_team": is_new_team,
 
457
  fn=on_register,
458
  inputs=[
459
  email,
460
+ name,
461
  affiliation,
462
  description,
463
  is_new_team_checkbox,
 
469
  ],
470
  outputs=[registration_status],
471
  )
 
 
 
 
 
 
components/registration/utils.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import logging
2
  import re
3
 
@@ -9,21 +10,17 @@ from components.utils import load_data_from_dataset
9
 
10
  logger = logging.getLogger(__name__)
11
 
12
-
13
  def validate_email_domain(email: str) -> bool:
14
  """Return True if email belongs to an academic or registered company domain."""
15
- academic_tlds = [
16
- ".edu",
17
- ".ac.uk",
18
- ".ac.",
19
- "university",
20
- "univ.",
21
- "institute",
22
- ".gov",
23
- ]
24
  return any(token in email.lower() for token in academic_tlds)
25
 
26
 
 
 
 
 
 
27
  def validate_email_format(email: str) -> bool:
28
  """
29
  Validate the email format.
@@ -40,35 +37,33 @@ def is_institutional_email(email: str) -> bool:
40
  return any(p in email.lower() for p in patterns)
41
 
42
 
43
- def get_user_teams(username: str) -> pd.DataFrame:
44
- """
45
- Return the teams associated with a given username.
46
 
47
- :param username:The unique hf username to query.
48
- :return: The teams associated with a given username as a DataFrame,
49
- with human-readable columns for role and registration status.
 
 
 
 
50
  """
51
- EMPTY_RESULT = pd.DataFrame(
52
- columns=["team_name", "is_leader", "registration_status"]
53
- )
54
 
55
- participants_df = load_data_from_dataset(
56
- REGISTRATION_REPO, PARTICIPANTS_FILE_NAME, PARTICIPANT_COLUMNS
57
- )
58
- if participants_df.empty:
59
- return EMPTY_RESULT
60
 
61
- existing_participant = participants_df[participants_df["username"] == username]
62
  if existing_participant.empty:
63
- return EMPTY_RESULT
64
 
65
- return existing_participant[["team_name", "is_leader", "registration_status"]]
 
 
66
 
 
 
67
 
68
- def rename_user_teams(result: pd.DataFrame) -> pd.DataFrame:
69
- result["role"] = result["is_leader"].map(
70
- {True: "Leader", False: "Member", "True": "Leader", "False": "Member"}
71
- )
72
- return result[["team_name", "role", "registration_status"]].rename(
73
- columns={"team_name": "Team Name"}
74
- )
 
1
+ import json
2
  import logging
3
  import re
4
 
 
10
 
11
  logger = logging.getLogger(__name__)
12
 
 
13
  def validate_email_domain(email: str) -> bool:
14
  """Return True if email belongs to an academic or registered company domain."""
15
+ academic_tlds = [".edu", ".ac.uk", ".ac.", "university", "univ.", "institute", ".gov"]
 
 
 
 
 
 
 
 
16
  return any(token in email.lower() for token in academic_tlds)
17
 
18
 
19
+ def get_team_membership_entry(team_name: str, is_leader: bool) -> dict[str, str | bool]:
20
+ """Return a dictionary with information about a team."""
21
+ return {"team_name": team_name, "is_leader": is_leader}
22
+
23
+
24
  def validate_email_format(email: str) -> bool:
25
  """
26
  Validate the email format.
 
37
  return any(p in email.lower() for p in patterns)
38
 
39
 
 
 
 
40
 
41
+ def _participant_team_names(participant_row: pd.Series) -> list[str]:
42
+ """Return list of team_name the participant already belongs to."""
43
+ memberships = json.loads(participant_row.get("team_memberships") or "[]")
44
+ return [m["team_name"] for m in memberships]
45
+
46
+
47
+ def get_user_teams(email: str) -> pd.DataFrame:
48
  """
49
+ Return the teams associated with a given email.
 
 
50
 
51
+ :param email: The email address to query.
52
+ :return: The teams associated with a given email as a DataFrame.
53
+ """
54
+ participants_df = load_data_from_dataset(REGISTRATION_REPO, PARTICIPANTS_FILE_NAME, PARTICIPANT_COLUMNS)
55
+ existing_participant = participants_df[participants_df["email"] == email]
56
 
 
57
  if existing_participant.empty:
58
+ return pd.DataFrame(columns=["team_name", "role"])
59
 
60
+ existing_teams = json.loads(
61
+ existing_participant.iloc[0].get("team_memberships") or "[]"
62
+ )
63
 
64
+ if not existing_teams:
65
+ return pd.DataFrame(columns=["team_name", "role"])
66
 
67
+ df = pd.DataFrame(existing_teams)
68
+ df["role"] = df["is_leader"].apply(lambda x: "Leader" if x else "Member")
69
+ return df[["team_name", "role"]]
 
 
 
 
components/submission/config.py CHANGED
@@ -23,10 +23,10 @@ You are allowed to submit up to {max} results per hypothesis.
23
  SUBMISSION_COLUMNS = [
24
  "submission_id",
25
  "team_name",
26
- "submission_username",
27
  "created_at",
28
  "hypothesis",
29
  "submission_note",
30
  "method",
31
  "file_path",
32
- ]
 
23
  SUBMISSION_COLUMNS = [
24
  "submission_id",
25
  "team_name",
26
+ "submission_email",
27
  "created_at",
28
  "hypothesis",
29
  "submission_note",
30
  "method",
31
  "file_path",
32
+ ]
components/submission/submission_page.py CHANGED
@@ -3,9 +3,8 @@ from components.submission.utils import (
3
  get_team_submission_count,
4
  submit_prediction,
5
  validate_user_registration,
6
- validate_user_team_registration,
7
  )
8
- from components.utils import check_team_has_leader, prefill_user_info
9
  from components.submission.config import (
10
  MAX_SUBMISSIONS_PER_HYPOTHESIS,
11
  SUBMISSION_PAGE_DESCRIPTION,
@@ -23,8 +22,8 @@ def validate_csv(uploaded_file: str) -> tuple[bool, list[str]]:
23
  return True, []
24
 
25
 
26
- def get_submission_page(gr, demo):
27
- with gr.TabItem("✉️ Submit", id=2):
28
 
29
  # ── Section header ──────────────────────────────────────────────────
30
  gr.Markdown(SUBMISSION_PAGE_DESCRIPTION)
@@ -33,26 +32,18 @@ def get_submission_page(gr, demo):
33
 
34
  with gr.Row():
35
  with gr.Column():
36
- gr.LoginButton()
37
- username = gr.Textbox(
38
- label="Username from your Hugging Face account *",
39
- placeholder="Fetching from HuggingFace...",
40
- interactive=False,
41
- )
42
- name = gr.Textbox(
43
- label="Full name from your Hugging Face account *",
44
- placeholder="Fetching from HuggingFace...",
45
- interactive=False,
46
- )
47
- email = gr.Textbox(
48
- label="Email *", placeholder="myemail@domain.com", interactive=True
49
- )
50
  team_name = gr.Textbox(
51
  label="Team name *",
52
  placeholder="My Awesome Lab",
53
  info="Enter your team name.",
54
  value="",
55
  )
 
 
 
 
 
 
56
  hypothesis_dropdown = gr.Dropdown(
57
  label="Hypothesis *",
58
  choices=PROBLEM_TYPES,
@@ -101,24 +92,22 @@ def get_submission_page(gr, demo):
101
 
102
  # -- Submission count display when team / hypothesis changes ---------
103
  def on_submission_team_or_hypothesis_change(
104
- submission_username: str, submission_team_name: str, hypothesis: str
105
  ):
106
- if submission_username is None or not submission_username.strip():
107
- return (
108
- " ❌You must be logged in with your HuggingFace account to register"
109
- )
110
  if (
111
  submission_team_name is None
 
 
112
  or not submission_team_name.strip()
113
  or not hypothesis
114
  ):
115
  return "❌ Enter your team name and select a hypothesis to see the number of results already submitted."
116
 
117
- is_user_registered = validate_user_team_registration(
118
- username=submission_username, team_name=submission_team_name
119
  )
120
  if not is_user_registered:
121
- return "❌ The username corresponding to the account you are logged in with is not registered as part of the given team. Please register on the registration panel."
122
 
123
  count = get_team_submission_count(submission_team_name, hypothesis)
124
  remaining = MAX_SUBMISSIONS_PER_HYPOTHESIS - count
@@ -136,14 +125,14 @@ def get_submission_page(gr, demo):
136
  for widget in [team_name, hypothesis_dropdown]:
137
  widget.change(
138
  fn=on_submission_team_or_hypothesis_change,
139
- inputs=[username, team_name, hypothesis_dropdown],
140
  outputs=[submission_count_display],
141
  )
142
 
143
  # -- Prediction file upload & validation -----------------------------
144
  def on_submit(
145
  submission_team_name: str,
146
- submission_username: str,
147
  submission_hypothesis: str,
148
  submission_file: str,
149
  submission_note: str,
@@ -153,7 +142,7 @@ def get_submission_page(gr, demo):
153
  success, status = on_validation(
154
  file=predictions_file,
155
  submission_team_name=submission_team_name,
156
- submission_username=submission_username,
157
  submission_hypothesis=submission_hypothesis,
158
  )
159
  if not success:
@@ -164,7 +153,7 @@ def get_submission_page(gr, demo):
164
  team_name=submission_team_name,
165
  hypothesis=submission_hypothesis,
166
  file_path=submission_file,
167
- uploader_username=submission_username,
168
  note=submission_note,
169
  link_to_methods=report_link,
170
  )
@@ -191,7 +180,7 @@ def get_submission_page(gr, demo):
191
  fn=on_submit,
192
  inputs=[
193
  team_name,
194
- username,
195
  hypothesis_dropdown,
196
  predictions_file,
197
  note,
@@ -202,41 +191,29 @@ def get_submission_page(gr, demo):
202
 
203
  def on_validation(
204
  file: str | None,
205
- submission_username: str,
206
  submission_team_name: str,
207
  submission_hypothesis: str,
208
  ) -> tuple[bool, str]:
209
  """Validate the given file according to the challenge's requirements."""
210
  errors: list[str] = []
211
- if not submission_username.strip():
212
- return False, gr.update(
213
- value=f"❌ You must be logged in with your HuggingFace account to register.",
214
- visible=True,
215
- )
216
  if not submission_team_name.strip():
217
  errors.append("Team name is required.")
 
 
218
  if submission_hypothesis is None:
219
  errors.append("Please select a hypothesis..")
220
  if errors:
221
  status = "❌ " + " | ".join(errors)
222
  return False, gr.update(value=status, visible=True)
223
 
224
- user_validation = validate_user_registration(
225
- username=submission_username, team_name=submission_team_name
226
  )
227
- if user_validation == "not registered":
228
- errors.append(
229
- "The username corresponding to the account you are logged in with is not registered as part of the given team. Please register on the registration panel."
230
- )
231
- elif user_validation == "pending":
232
- errors.append(
233
- "Your registration is still pending. Please wait until your registration is validated or contact the challenge organizers.."
234
- )
235
- elif user_validation == "not validated":
236
  errors.append(
237
- "Your registration was not accepted by the organizers. You can't submit your submission. Contact your organizers if you think it was an error."
238
  )
239
-
240
  if file is None:
241
  errors.append("Please upload a CSV file.")
242
 
@@ -279,12 +256,6 @@ def get_submission_page(gr, demo):
279
 
280
  validation_button.click(
281
  fn=on_validation,
282
- inputs=[predictions_file, username, team_name, hypothesis_dropdown],
283
  outputs=[is_valid, validation_status],
284
  )
285
-
286
- demo.load(
287
- fn=prefill_user_info,
288
- inputs=[],
289
- outputs=[username, name, email],
290
- )
 
3
  get_team_submission_count,
4
  submit_prediction,
5
  validate_user_registration,
 
6
  )
7
+ from components.utils import check_team_has_leader
8
  from components.submission.config import (
9
  MAX_SUBMISSIONS_PER_HYPOTHESIS,
10
  SUBMISSION_PAGE_DESCRIPTION,
 
22
  return True, []
23
 
24
 
25
+ def get_submission_page(gr):
26
+ with gr.TabItem("✉️ Submit"):
27
 
28
  # ── Section header ──────────────────────────────────────────────────
29
  gr.Markdown(SUBMISSION_PAGE_DESCRIPTION)
 
32
 
33
  with gr.Row():
34
  with gr.Column():
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  team_name = gr.Textbox(
36
  label="Team name *",
37
  placeholder="My Awesome Lab",
38
  info="Enter your team name.",
39
  value="",
40
  )
41
+ uploader_email = gr.Textbox(
42
+ label=" Email *",
43
+ info="Enter your email.",
44
+ placeholder="you@university.edu",
45
+ value="",
46
+ )
47
  hypothesis_dropdown = gr.Dropdown(
48
  label="Hypothesis *",
49
  choices=PROBLEM_TYPES,
 
92
 
93
  # -- Submission count display when team / hypothesis changes ---------
94
  def on_submission_team_or_hypothesis_change(
95
+ submission_email: str, submission_team_name: str, hypothesis: str
96
  ):
 
 
 
 
97
  if (
98
  submission_team_name is None
99
+ or submission_email is None
100
+ or not submission_email.strip()
101
  or not submission_team_name.strip()
102
  or not hypothesis
103
  ):
104
  return "❌ Enter your team name and select a hypothesis to see the number of results already submitted."
105
 
106
+ is_user_registered = validate_user_registration(
107
+ email=submission_email, team_name=submission_team_name
108
  )
109
  if not is_user_registered:
110
+ return "❌ The given email is not registered as part of the given team. Please provide a registered email and team or register on the registration panel."
111
 
112
  count = get_team_submission_count(submission_team_name, hypothesis)
113
  remaining = MAX_SUBMISSIONS_PER_HYPOTHESIS - count
 
125
  for widget in [team_name, hypothesis_dropdown]:
126
  widget.change(
127
  fn=on_submission_team_or_hypothesis_change,
128
+ inputs=[uploader_email, team_name, hypothesis_dropdown],
129
  outputs=[submission_count_display],
130
  )
131
 
132
  # -- Prediction file upload & validation -----------------------------
133
  def on_submit(
134
  submission_team_name: str,
135
+ submission_email: str,
136
  submission_hypothesis: str,
137
  submission_file: str,
138
  submission_note: str,
 
142
  success, status = on_validation(
143
  file=predictions_file,
144
  submission_team_name=submission_team_name,
145
+ submission_email=submission_email,
146
  submission_hypothesis=submission_hypothesis,
147
  )
148
  if not success:
 
153
  team_name=submission_team_name,
154
  hypothesis=submission_hypothesis,
155
  file_path=submission_file,
156
+ uploader_email=submission_email,
157
  note=submission_note,
158
  link_to_methods=report_link,
159
  )
 
180
  fn=on_submit,
181
  inputs=[
182
  team_name,
183
+ uploader_email,
184
  hypothesis_dropdown,
185
  predictions_file,
186
  note,
 
191
 
192
  def on_validation(
193
  file: str | None,
194
+ submission_email: str,
195
  submission_team_name: str,
196
  submission_hypothesis: str,
197
  ) -> tuple[bool, str]:
198
  """Validate the given file according to the challenge's requirements."""
199
  errors: list[str] = []
 
 
 
 
 
200
  if not submission_team_name.strip():
201
  errors.append("Team name is required.")
202
+ if not submission_email.strip():
203
+ errors.append("Your email is required for compliance notifications.")
204
  if submission_hypothesis is None:
205
  errors.append("Please select a hypothesis..")
206
  if errors:
207
  status = "❌ " + " | ".join(errors)
208
  return False, gr.update(value=status, visible=True)
209
 
210
+ is_user_registered = validate_user_registration(
211
+ email=submission_email, team_name=submission_team_name
212
  )
213
+ if not is_user_registered:
 
 
 
 
 
 
 
 
214
  errors.append(
215
+ "Please provide your email address and the team you're registered into and the hypothesis corresponding to your hypothesis."
216
  )
 
217
  if file is None:
218
  errors.append("Please upload a CSV file.")
219
 
 
256
 
257
  validation_button.click(
258
  fn=on_validation,
259
+ inputs=[predictions_file, uploader_email, team_name, hypothesis_dropdown],
260
  outputs=[is_valid, validation_status],
261
  )
 
 
 
 
 
 
components/submission/utils.py CHANGED
@@ -5,7 +5,6 @@ import pandas as pd
5
  from huggingface_hub import HfApi
6
 
7
  from about import TOKEN, REGISTRATION_REPO
8
- from components.registration.config import PARTICIPANTS_FILE_NAME, PARTICIPANT_COLUMNS
9
  from components.registration.utils import get_user_teams
10
  from components.submission.config import SUBMISSION_COLUMNS, SUBMISSIONS_FILE_NAME
11
  from components.utils import load_data_from_dataset, push_data_to_dataset
@@ -17,7 +16,7 @@ def submit_prediction(
17
  team_name: str,
18
  hypothesis: str,
19
  file_path: str,
20
- uploader_username: str,
21
  note: str | None,
22
  link_to_methods: str | None,
23
  ) -> tuple[bool, str]:
@@ -47,7 +46,7 @@ def submit_prediction(
47
  {
48
  "submission_id": submission_id,
49
  "team_name": team_name,
50
- "submission_username": uploader_username,
51
  "created_at": datetime.now(timezone.utc).isoformat(),
52
  "hypothesis": hypothesis,
53
  "submission_note": note or "",
@@ -80,40 +79,13 @@ def get_team_submission_count(team_name: str, hypothesis: str) -> int:
80
  ].shape[0]
81
 
82
 
83
- def validate_user_team_registration(username: str, team_name: str) -> bool:
84
  """
85
- Verify that the given username is registered as part of the team with the given team_name.
86
 
87
- :param username: The username to validate.
88
  :param team_name: The team to validate.
89
- :return: True is the username is registered as part of the team with the given team_name. False otherwise.
90
  """
91
- user_teams = get_user_teams(username=username)
92
  return len(user_teams[user_teams["team_name"] == team_name]) > 0
93
-
94
-
95
- def validate_user_registration(username: str, team_name: str) -> str:
96
- """
97
- Verify that the given username is validated as part of the team with the given team_name.
98
-
99
- :param username: The username to validate.
100
- :param team_name: The team to validate.
101
- :return: True is the username is registered and validated as part of the team with the given team_name. False otherwise.
102
- """
103
- participants_df = load_data_from_dataset(
104
- REGISTRATION_REPO, PARTICIPANTS_FILE_NAME, PARTICIPANT_COLUMNS
105
- )
106
- if participants_df.empty:
107
- return "not registered"
108
- record = participants_df[
109
- (participants_df["username"] == username)
110
- & (participants_df["team_name"] == team_name)
111
- ]
112
- if record.empty:
113
- return "not registered"
114
- elif record.iloc[0]["registration_status"] == "Validated":
115
- return "validated"
116
- elif record.iloc[0]["registration_status"] == "Pending":
117
- return "pending"
118
- else:
119
- return "not validated"
 
5
  from huggingface_hub import HfApi
6
 
7
  from about import TOKEN, REGISTRATION_REPO
 
8
  from components.registration.utils import get_user_teams
9
  from components.submission.config import SUBMISSION_COLUMNS, SUBMISSIONS_FILE_NAME
10
  from components.utils import load_data_from_dataset, push_data_to_dataset
 
16
  team_name: str,
17
  hypothesis: str,
18
  file_path: str,
19
+ uploader_email: str,
20
  note: str | None,
21
  link_to_methods: str | None,
22
  ) -> tuple[bool, str]:
 
46
  {
47
  "submission_id": submission_id,
48
  "team_name": team_name,
49
+ "submission_email": uploader_email,
50
  "created_at": datetime.now(timezone.utc).isoformat(),
51
  "hypothesis": hypothesis,
52
  "submission_note": note or "",
 
79
  ].shape[0]
80
 
81
 
82
+ def validate_user_registration(email: str, team_name: str) -> bool:
83
  """
84
+ Verify that the given email is registered as part of the team with the given team_name.
85
 
86
+ :param email: The email to validate.
87
  :param team_name: The team to validate.
88
+ :return: True is the email is registered as part of the team with the given team_name. False otherwise.
89
  """
90
+ user_teams = get_user_teams(email=email)
91
  return len(user_teams[user_teams["team_name"] == team_name]) > 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
components/utils.py CHANGED
@@ -4,10 +4,7 @@ import pathlib
4
  import tempfile
5
  import json
6
  from pathlib import Path
7
- import httpx
8
 
9
-
10
- import gradio
11
  import gradio as gr
12
  import pandas as pd
13
  from huggingface_hub import hf_hub_download, HfApi
@@ -166,30 +163,4 @@ def check_team_has_leader(team_name: str) -> bool:
166
  team = get_team(teams_df, team_name)
167
  if team is None:
168
  return False
169
- return bool((team.get("leader_username") or "").strip())
170
-
171
-
172
- def prefill_user_info(
173
- oauth_profile: gradio.OAuthProfile | None,
174
- oauth_token: gradio.OAuthToken | None,
175
- ):
176
- empty = lambda: gr.update(value="", placeholder="Log in with HuggingFace first")
177
- if oauth_profile is None or oauth_token is None:
178
- return empty(), empty(), empty()
179
-
180
- try:
181
- resp = httpx.get(
182
- "https://huggingface.co/oauth/userinfo",
183
- headers={"Authorization": f"Bearer {oauth_token.token}"},
184
- timeout=5,
185
- )
186
- resp.raise_for_status()
187
- email = resp.json().get("email", "")
188
- except Exception:
189
- email = ''
190
-
191
- return (
192
- gr.update(value=oauth_profile.username),
193
- gr.update(value=oauth_profile.name),
194
- gr.update(value=email),
195
- )
 
4
  import tempfile
5
  import json
6
  from pathlib import Path
 
7
 
 
 
8
  import gradio as gr
9
  import pandas as pd
10
  from huggingface_hub import hf_hub_download, HfApi
 
163
  team = get_team(teams_df, team_name)
164
  if team is None:
165
  return False
166
+ return bool((team.get("leader_email") or "").strip())
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
packages.txt DELETED
@@ -1,3 +0,0 @@
1
- build-essential
2
- cmake
3
- libnetcdf-dev
 
 
 
 
requirements.txt CHANGED
@@ -1,9 +1,7 @@
1
- gradio==5.50.0
2
  datasets
3
  huggingface_hub
4
- httpx
5
  gradio-leaderboard
6
  plotly
7
  dotenv
8
- pandas
9
- gradio[oauth]
 
1
+ gradio
2
  datasets
3
  huggingface_hub
 
4
  gradio-leaderboard
5
  plotly
6
  dotenv
7
+ pandas