ha251 commited on
Commit
6ca62b8
·
verified ·
1 Parent(s): e8915ff

Update miniapp_leaderboard.py

Browse files
Files changed (1) hide show
  1. miniapp_leaderboard.py +104 -55
miniapp_leaderboard.py CHANGED
@@ -4,42 +4,34 @@ import json
4
  import os
5
  import re
6
  import uuid
 
7
 
8
  import gradio as gr
9
  import pandas as pd
10
  from huggingface_hub import HfApi, hf_hub_download
11
 
12
- APP_NAME = "MiniApp"
13
 
14
  HF_TOKEN = os.environ.get("HF_TOKEN")
15
  LEADERBOARD_DATASET = os.environ.get("LEADERBOARD_DATASET", "").strip()
16
- OWNER_REVIEW_TOKEN = os.environ.get("OWNER_REVIEW_TOKEN", "").strip()
17
 
18
  PENDING_PREFIX = "pending/"
19
  APPROVED_PREFIX = "approved/"
20
 
21
- COLUMNS = [
22
- "model name",
23
- "model family",
24
- "avg",
25
- "easy",
26
- "mid",
27
- "hard",
28
- "submitted_at",
29
- ]
30
-
31
- NUMERIC_COLS = ["avg", "easy", "mid", "hard"]
32
 
 
 
33
 
34
  def _api():
35
  return HfApi(token=HF_TOKEN)
36
 
37
-
38
  def _slug(s: str):
39
  s = re.sub(r"[^a-z0-9]+", "-", (s or "").lower())
40
  return s.strip("-") or "model"
41
 
42
-
43
  def _load_df(prefix: str):
44
  if not HF_TOKEN or not LEADERBOARD_DATASET:
45
  return pd.DataFrame(columns=COLUMNS)
@@ -52,7 +44,6 @@ def _load_df(prefix: str):
52
 
53
  files = [f for f in files if f.startswith(prefix) and f.endswith(".json")]
54
  rows = []
55
-
56
  for f in files:
57
  try:
58
  path = hf_hub_download(
@@ -73,31 +64,87 @@ def _load_df(prefix: str):
73
  for c in COLUMNS:
74
  if c not in df.columns:
75
  df[c] = ""
76
-
77
  for c in NUMERIC_COLS:
78
  df[c] = pd.to_numeric(df[c], errors="coerce")
79
 
80
  df = df.sort_values(by="avg", ascending=False)
81
  return df[COLUMNS]
82
 
83
-
84
  def refresh():
85
  return _load_df(APPROVED_PREFIX)
86
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
- def submit(model_name, model_family, email, zip_file):
89
- if not model_name or not model_family or not email or zip_file is None:
90
- return "All fields are required.", refresh()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
91
 
 
 
 
92
  if not zip_file.name.endswith(".zip"):
93
  return "Please upload a .zip file.", refresh()
94
 
 
 
 
 
 
 
 
 
 
 
95
  now = datetime.datetime.utcnow().isoformat() + "Z"
96
  safe_model = _slug(model_name)
97
  nonce = uuid.uuid4().hex[:6]
98
 
99
- json_path = f"{PENDING_PREFIX}{now}-{safe_model}-{nonce}.json"
100
- zip_path = f"{PENDING_PREFIX}{now}-{safe_model}-{nonce}.zip"
 
 
 
101
 
102
  payload = {
103
  "model name": model_name,
@@ -107,65 +154,67 @@ def submit(model_name, model_family, email, zip_file):
107
  "mid": 0,
108
  "hard": 0,
109
  "submitted_at": now,
110
- "email": email,
 
111
  }
112
 
113
- api = _api()
114
-
115
- # 上传 JSON
116
  api.upload_file(
117
  repo_id=LEADERBOARD_DATASET,
118
  repo_type="dataset",
119
- path_or_fileobj=io.BytesIO(
120
- json.dumps(payload, indent=2).encode("utf-8")
121
- ),
122
  path_in_repo=json_path,
123
- commit_message=f"submit {model_name}",
124
  )
125
 
126
- # 上传 ZIP
127
  api.upload_file(
128
  repo_id=LEADERBOARD_DATASET,
129
  repo_type="dataset",
130
  path_or_fileobj=zip_file,
131
  path_in_repo=zip_path,
132
- commit_message=f"upload zip {model_name}",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  )
134
 
135
  return "Submitted. Waiting for review.", refresh()
136
 
137
- with gr.Blocks(
138
- title=f"{APP_NAME} leaderboard",
139
- css="""
140
- /* 全局字体变大(影响 Markdown、按钮、输入框等) */
141
- :root { --body-text-size: 18px; }
142
- .gradio-container { font-size: var(--body-text-size); }
143
-
144
- /* 表格字体、行高 */
145
- #leaderboard_table table { font-size: 18px; }
146
- #leaderboard_table th, #leaderboard_table td { padding: 10px 12px; }
147
-
148
- /* Dataframe 内部输入/渲染(不同版本 gradio 结构可能略不同,这两条通常有用) */
149
- #leaderboard_table .cell-wrap,
150
- #leaderboard_table .ag-cell { font-size: 18px !important; }
151
- """
152
- ) as demo:
153
  gr.Markdown(f"# {APP_NAME} Leaderboard")
154
 
155
  leaderboard = gr.Dataframe(
156
  value=_load_df(APPROVED_PREFIX),
157
  interactive=False,
158
  wrap=True,
159
- elem_id="leaderboard_table", # 关键:给它一个 id 方便写 CSS
160
- elem_classes="full-width",
161
  )
 
162
  refresh_btn = gr.Button("Refresh")
163
 
164
- gr.Markdown("## Submit")
165
 
166
  model_name = gr.Textbox(label="Model name")
167
  model_family = gr.Textbox(label="Model family")
168
- email = gr.Textbox(label="Email")
169
  zip_file = gr.File(label="Upload zip", file_types=[".zip"])
170
  submit_btn = gr.Button("Submit", variant="primary")
171
  status = gr.Markdown()
@@ -173,8 +222,8 @@ with gr.Blocks(
173
  refresh_btn.click(refresh, outputs=[leaderboard])
174
  submit_btn.click(
175
  submit,
176
- inputs=[model_name, model_family, email, zip_file],
177
  outputs=[status, leaderboard],
178
  )
179
 
180
- demo.launch()
 
4
  import os
5
  import re
6
  import uuid
7
+ import requests
8
 
9
  import gradio as gr
10
  import pandas as pd
11
  from huggingface_hub import HfApi, hf_hub_download
12
 
13
+ APP_NAME = "MiniApp2"
14
 
15
  HF_TOKEN = os.environ.get("HF_TOKEN")
16
  LEADERBOARD_DATASET = os.environ.get("LEADERBOARD_DATASET", "").strip()
 
17
 
18
  PENDING_PREFIX = "pending/"
19
  APPROVED_PREFIX = "approved/"
20
 
21
+ RESEND_API_KEY = os.environ.get("RESEND_API_KEY", "").strip()
22
+ NOTIFY_EMAIL_TO = os.environ.get("NOTIFY_EMAIL_TO", "").strip()
23
+ NOTIFY_EMAIL_FROM = os.environ.get("NOTIFY_EMAIL_FROM", "").strip()
 
 
 
 
 
 
 
 
24
 
25
+ COLUMNS = ["model name","model family","avg","easy","mid","hard","submitted_at"]
26
+ NUMERIC_COLS = ["avg","easy","mid","hard"]
27
 
28
  def _api():
29
  return HfApi(token=HF_TOKEN)
30
 
 
31
  def _slug(s: str):
32
  s = re.sub(r"[^a-z0-9]+", "-", (s or "").lower())
33
  return s.strip("-") or "model"
34
 
 
35
  def _load_df(prefix: str):
36
  if not HF_TOKEN or not LEADERBOARD_DATASET:
37
  return pd.DataFrame(columns=COLUMNS)
 
44
 
45
  files = [f for f in files if f.startswith(prefix) and f.endswith(".json")]
46
  rows = []
 
47
  for f in files:
48
  try:
49
  path = hf_hub_download(
 
64
  for c in COLUMNS:
65
  if c not in df.columns:
66
  df[c] = ""
 
67
  for c in NUMERIC_COLS:
68
  df[c] = pd.to_numeric(df[c], errors="coerce")
69
 
70
  df = df.sort_values(by="avg", ascending=False)
71
  return df[COLUMNS]
72
 
 
73
  def refresh():
74
  return _load_df(APPROVED_PREFIX)
75
 
76
+ def _today_utc():
77
+ return datetime.datetime.utcnow().date().isoformat() # YYYY-MM-DD
78
+
79
+ def _send_email_resend(subject: str, text: str):
80
+ # 可替换为 SendGrid/Mailgun
81
+ if not (RESEND_API_KEY and NOTIFY_EMAIL_TO and NOTIFY_EMAIL_FROM):
82
+ return # 没配置就不发
83
+
84
+ requests.post(
85
+ "https://api.resend.com/emails",
86
+ headers={
87
+ "Authorization": f"Bearer {RESEND_API_KEY}",
88
+ "Content-Type": "application/json",
89
+ },
90
+ json={
91
+ "from": NOTIFY_EMAIL_FROM,
92
+ "to": [NOTIFY_EMAIL_TO],
93
+ "subject": subject,
94
+ "text": text,
95
+ },
96
+ timeout=20,
97
+ )
98
 
99
+ def _already_submitted_today(api: HfApi, day: str, username: str) -> bool:
100
+ # 用“标记文件”判断:pending/YYYY-MM-DD/<username>/_submitted.json
101
+ marker = f"{PENDING_PREFIX}{day}/{username}/_submitted.json"
102
+ try:
103
+ files = api.list_repo_files(repo_id=LEADERBOARD_DATASET, repo_type="dataset")
104
+ return marker in files
105
+ except Exception:
106
+ # 列表失败时宁可放行或宁可拒绝,二选一;这里选择拒绝更安全
107
+ return True
108
+
109
+ def submit(model_name, model_family, zip_file, request: gr.Request):
110
+ # 1) 必须登录:从 request 拿 HF username
111
+ username = None
112
+ try:
113
+ # gradio 在 HF Spaces 登录场景下,通常能从 request 得到用户信息
114
+ # 不同版本字段可能略有差异;这里做多种兜底
115
+ username = getattr(request, "username", None) \
116
+ or (request.headers.get("x-forwarded-user") if request else None)
117
+ except Exception:
118
+ username = None
119
+
120
+ if not username:
121
+ return "Please sign in first (HF login) before submitting.", refresh()
122
 
123
+ # 2) 基础校验
124
+ if not model_name or not model_family or zip_file is None:
125
+ return "All fields are required.", refresh()
126
  if not zip_file.name.endswith(".zip"):
127
  return "Please upload a .zip file.", refresh()
128
 
129
+ if not HF_TOKEN or not LEADERBOARD_DATASET:
130
+ return "Server is not configured (HF_TOKEN / LEADERBOARD_DATASET).", refresh()
131
+
132
+ api = _api()
133
+ day = _today_utc()
134
+
135
+ # 3) 每天一次限制
136
+ if _already_submitted_today(api, day, username):
137
+ return f"Limit: you can only submit once per day. (user={username}, day={day})", refresh()
138
+
139
  now = datetime.datetime.utcnow().isoformat() + "Z"
140
  safe_model = _slug(model_name)
141
  nonce = uuid.uuid4().hex[:6]
142
 
143
+ # 4) 按日期/用户名组织路径
144
+ base_dir = f"{PENDING_PREFIX}{day}/{username}/"
145
+ json_path = f"{base_dir}{now}-{safe_model}-{nonce}.json"
146
+ zip_path = f"{base_dir}{now}-{safe_model}-{nonce}.zip"
147
+ marker_path = f"{base_dir}_submitted.json"
148
 
149
  payload = {
150
  "model name": model_name,
 
154
  "mid": 0,
155
  "hard": 0,
156
  "submitted_at": now,
157
+ "username": username,
158
+ "day": day,
159
  }
160
 
161
+ # 5) 上传 JSON / ZIP / marker(marker 用于每天一次)
 
 
162
  api.upload_file(
163
  repo_id=LEADERBOARD_DATASET,
164
  repo_type="dataset",
165
+ path_or_fileobj=io.BytesIO(json.dumps(payload, indent=2).encode("utf-8")),
 
 
166
  path_in_repo=json_path,
167
+ commit_message=f"submit {model_name} by {username}",
168
  )
169
 
 
170
  api.upload_file(
171
  repo_id=LEADERBOARD_DATASET,
172
  repo_type="dataset",
173
  path_or_fileobj=zip_file,
174
  path_in_repo=zip_path,
175
+ commit_message=f"upload zip {model_name} by {username}",
176
+ )
177
+
178
+ api.upload_file(
179
+ repo_id=LEADERBOARD_DATASET,
180
+ repo_type="dataset",
181
+ path_or_fileobj=io.BytesIO(json.dumps({"submitted_at": now, "username": username, "day": day}, indent=2).encode("utf-8")),
182
+ path_in_repo=marker_path,
183
+ commit_message=f"marker {day} {username}",
184
+ )
185
+
186
+ # 6) 邮件提醒
187
+ _send_email_resend(
188
+ subject=f"[{APP_NAME}] New submission from {username} ({day})",
189
+ text=(
190
+ f"New submission received.\n\n"
191
+ f"user: {username}\n"
192
+ f"day: {day}\n"
193
+ f"model: {model_name}\n"
194
+ f"family: {model_family}\n"
195
+ f"json: {json_path}\n"
196
+ f"zip: {zip_path}\n"
197
+ ),
198
  )
199
 
200
  return "Submitted. Waiting for review.", refresh()
201
 
202
+
203
+ with gr.Blocks(title=f"{APP_NAME} leaderboard") as demo:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  gr.Markdown(f"# {APP_NAME} Leaderboard")
205
 
206
  leaderboard = gr.Dataframe(
207
  value=_load_df(APPROVED_PREFIX),
208
  interactive=False,
209
  wrap=True,
 
 
210
  )
211
+
212
  refresh_btn = gr.Button("Refresh")
213
 
214
+ gr.Markdown("## Submit (login required)")
215
 
216
  model_name = gr.Textbox(label="Model name")
217
  model_family = gr.Textbox(label="Model family")
 
218
  zip_file = gr.File(label="Upload zip", file_types=[".zip"])
219
  submit_btn = gr.Button("Submit", variant="primary")
220
  status = gr.Markdown()
 
222
  refresh_btn.click(refresh, outputs=[leaderboard])
223
  submit_btn.click(
224
  submit,
225
+ inputs=[model_name, model_family, zip_file],
226
  outputs=[status, leaderboard],
227
  )
228
 
229
+ demo.launch()