studyOverflow commited on
Commit
7064e17
·
verified ·
1 Parent(s): 73321fe

i18n: UI copy to Simplified Chinese

Browse files
Files changed (1) hide show
  1. app.py +43 -53
app.py CHANGED
@@ -94,7 +94,7 @@ def _extract_prompt(task: dict[str, Any]) -> str:
94
  val = prompts.get(level)
95
  if isinstance(val, list) and val:
96
  n = len(val)
97
- return "\n\n".join(f"— Segment {i}/{n} —\n{seg}" for i, seg in enumerate(val, 1))
98
  if isinstance(val, str) and val:
99
  return val
100
  return "(no prompt found)"
@@ -247,9 +247,9 @@ def _stats_md() -> str:
247
  total = len(POOL)
248
  pct = 100.0 * n_sub / total if total else 0.0
249
  return (
250
- f"**Global progress**: {n_sub} / {total} submitted ({pct:.1f}%)"
251
- f" {n_pend} currently being annotated by someone"
252
- f" {total - n_sub - n_pend} left"
253
  )
254
 
255
 
@@ -257,9 +257,9 @@ def _meta_md(model: str, task: dict[str, Any], state: dict) -> str:
257
  done_by_me = state.get("submitted_count", 0)
258
  claimed_n = state.get("claimed_n", 0)
259
  lines = [
260
- f"**Your submissions this session**: {done_by_me} (claimed {claimed_n} so far)",
261
- f"**Model**: `{model}`",
262
- f"**task_id**: `{task['task_id']}`",
263
  ]
264
  return "\n\n".join(lines)
265
 
@@ -268,41 +268,36 @@ def _render_video_html(url: str) -> str:
268
  return (
269
  f'<video src="{url}" controls autoplay loop muted playsinline '
270
  f'style="width:100%;height:520px;background:#000;border-radius:8px;object-fit:contain;">'
271
- f'Your browser does not support HTML5 video.</video>'
272
  )
273
 
274
 
275
  PLACEHOLDER_VIDEO = (
276
  "<div style='height:520px;display:flex;align-items:center;justify-content:center;"
277
  "color:#888;background:#111;border-radius:8px;'>"
278
- "Enter your name and click <b>Start</b> to load a video."
279
  "</div>"
280
  )
281
 
282
  ALL_DONE_VIDEO = (
283
  "<div style='height:520px;display:flex;align-items:center;justify-content:center;"
284
  "color:#4a7;background:#111;border-radius:8px;font-size:18px;'>"
285
- "🎉 All items have been annotated. Thank you!"
286
  "</div>"
287
  )
288
 
289
 
290
  def _next_item_for(state: dict) -> tuple[str, str, str, dict]:
291
- """Advance `state` to the next unclaimed item and return render strings.
292
-
293
- Returns (video_html, meta_md, prompt_text, updated_state).
294
- """
295
  annotator = state["annotator"]
296
  order = state["order"]
297
  idx = state.get("idx", 0)
298
 
299
- # Release the currently-held claim if any (e.g. after submit the caller
300
- # already released it; on first call there's nothing to release).
301
  new_idx, mt = _claim_next(annotator, order, idx)
302
  state["idx"] = new_idx
303
  if mt is None:
304
  state["current"] = None
305
- return ALL_DONE_VIDEO, "**All done.** Nothing left in the pool.", "", state
306
 
307
  state["current"] = mt
308
  state["claimed_n"] = state.get("claimed_n", 0) + 1
@@ -325,18 +320,15 @@ def start_session(annotator: str, state: dict):
325
  if not annotator:
326
  return (
327
  state, PLACEHOLDER_VIDEO, "", "",
328
- "⚠️ Please enter a name first.", _stats_md(),
329
  )
330
- # Release any claim an older session of this user still holds in-memory.
331
- # (We can't detect cross-browser sessions of the same user, but within one
332
- # process a user holding an old claim and logging in again will free it.)
333
  order = list(range(len(POOL)))
334
  rng = random.Random(f"{annotator}-{int(time.time())}-{uuid.uuid4().hex}")
335
  rng.shuffle(order)
336
  state = {"annotator": annotator, "order": order, "idx": 0,
337
  "current": None, "submitted_count": 0, "claimed_n": 0}
338
  video_html, meta, prompt, state = _next_item_for(state)
339
- status = f"✅ Welcome `{annotator}`. Good luck!"
340
  return state, video_html, meta, prompt, status, _stats_md()
341
 
342
 
@@ -344,15 +336,14 @@ def submit_and_next(state: dict, verdict: str, note: str):
344
  """Record current claim as submitted, then advance."""
345
  if not state or "annotator" not in state:
346
  return (
347
- state, PLACEHOLDER_VIDEO, "", "", "No", "",
348
- "⚠️ Please log in first.", _stats_md(),
349
  )
350
  current = state.get("current")
351
  if current is None:
352
- # No active claim — nothing to submit.
353
  return (
354
- state, ALL_DONE_VIDEO, "**All done.**", "", "No", "",
355
- "No active item to submit.", _stats_md(),
356
  )
357
  model, task_id = current
358
  record = {
@@ -362,8 +353,8 @@ def submit_and_next(state: dict, verdict: str, note: str):
362
  "process_id": PROCESS_ID,
363
  "model": model,
364
  "task_id": task_id,
365
- "memory_issue": verdict == "Yes", # boolean
366
- "verdict": verdict, # "Yes" / "No" raw
367
  "note": (note or "").strip(),
368
  }
369
  _append_annotation(record)
@@ -375,9 +366,9 @@ def submit_and_next(state: dict, verdict: str, note: str):
375
  video_html, meta, prompt, state = _next_item_for(state)
376
  return (
377
  state, video_html, meta, prompt,
378
- "No", # reset verdict
379
- "", # reset note
380
- f"✅ Submitted ({state['submitted_count']}) next",
381
  _stats_md(),
382
  )
383
 
@@ -386,8 +377,8 @@ def skip_and_next(state: dict):
386
  """Release the current claim back to the pool and advance."""
387
  if not state or "annotator" not in state:
388
  return (
389
- state, PLACEHOLDER_VIDEO, "", "", "No", "",
390
- "⚠️ Please log in first.", _stats_md(),
391
  )
392
  current = state.get("current")
393
  if current is not None:
@@ -398,8 +389,8 @@ def skip_and_next(state: dict):
398
  video_html, meta, prompt, state = _next_item_for(state)
399
  return (
400
  state, video_html, meta, prompt,
401
- "No", "",
402
- f"⏭️ Skipped. Your submissions: {state.get('submitted_count', 0)}",
403
  _stats_md(),
404
  )
405
 
@@ -412,14 +403,13 @@ CUSTOM_CSS = """
412
  #prompt_box textarea { height: 480px !important; overflow-y: auto !important; }
413
  """
414
 
415
- with gr.Blocks(title="MBench-V Annotation", theme=gr.themes.Soft(),
416
  css=CUSTOM_CSS) as demo:
417
  gr.Markdown(
418
  """
419
- # 🎬 MBench-V Annotation — *Does this video exhibit a memory issue?*
420
 
421
- Watch the video (left) against its intended 5-segment prompt (right) and
422
- answer **Yes** or **No**.
423
  """
424
  )
425
 
@@ -429,37 +419,37 @@ with gr.Blocks(title="MBench-V Annotation", theme=gr.themes.Soft(),
429
 
430
  with gr.Row():
431
  annotator_in = gr.Textbox(
432
- label="Annotator name", placeholder="e.g. alice", scale=4, autofocus=True,
433
  )
434
- login_btn = gr.Button("Start", variant="primary", scale=1)
435
 
436
- status_md = gr.Markdown("_Not started yet._")
437
 
438
- # ========= Top row: video prompt =========
439
  with gr.Row(equal_height=True):
440
  with gr.Column(scale=3):
441
- video = gr.HTML(value=PLACEHOLDER_VIDEO, label="Generated video")
442
  with gr.Column(scale=2):
443
  prompt_tb = gr.Textbox(
444
- label="Generation prompt (5 segments)",
445
  lines=22, max_lines=22,
446
  interactive=False, show_copy_button=True,
447
  elem_id="prompt_box",
448
  )
449
 
450
- # ========= Bottom row: meta verdict =========
451
  with gr.Row():
452
  with gr.Column(scale=3):
453
- meta_md = gr.Markdown("_No task loaded yet._")
454
  with gr.Column(scale=2):
455
  verdict = gr.Radio(
456
- choices=["No", "Yes"], value="No",
457
- label="Does this video exhibit a memory issue?",
458
  )
459
- note = gr.Textbox(label="Note (optional)", lines=2)
460
  with gr.Row():
461
- submit_btn = gr.Button("✅ Submit & Next", variant="primary")
462
- skip_btn = gr.Button("⏭️ Skip")
463
 
464
  # ========= Wiring =========
465
  login_outputs = [state, video, meta_md, prompt_tb, status_md, stats_md]
 
94
  val = prompts.get(level)
95
  if isinstance(val, list) and val:
96
  n = len(val)
97
+ return "\n\n".join(f"— {i}/{n} —\n{seg}" for i, seg in enumerate(val, 1))
98
  if isinstance(val, str) and val:
99
  return val
100
  return "(no prompt found)"
 
247
  total = len(POOL)
248
  pct = 100.0 * n_sub / total if total else 0.0
249
  return (
250
+ f"**全局进度**:已提交 {n_sub} / {total}{pct:.1f}%"
251
+ f" 正在被其他人标注 {n_pend}"
252
+ f" 剩余 {total - n_sub - n_pend}"
253
  )
254
 
255
 
 
257
  done_by_me = state.get("submitted_count", 0)
258
  claimed_n = state.get("claimed_n", 0)
259
  lines = [
260
+ f"**本次会话已提交**{done_by_me} 条(共已领取 {claimed_n} 条)",
261
+ f"**模型**`{model}`",
262
+ f"**task_id**`{task['task_id']}`",
263
  ]
264
  return "\n\n".join(lines)
265
 
 
268
  return (
269
  f'<video src="{url}" controls autoplay loop muted playsinline '
270
  f'style="width:100%;height:520px;background:#000;border-radius:8px;object-fit:contain;">'
271
+ f'您的浏览器不支持 HTML5 视频。</video>'
272
  )
273
 
274
 
275
  PLACEHOLDER_VIDEO = (
276
  "<div style='height:520px;display:flex;align-items:center;justify-content:center;"
277
  "color:#888;background:#111;border-radius:8px;'>"
278
+ "请先输入名字并点击 <b>开始</b> 加载视频。"
279
  "</div>"
280
  )
281
 
282
  ALL_DONE_VIDEO = (
283
  "<div style='height:520px;display:flex;align-items:center;justify-content:center;"
284
  "color:#4a7;background:#111;border-radius:8px;font-size:18px;'>"
285
+ "🎉 所有视频都已标注完成,感谢您的参与!"
286
  "</div>"
287
  )
288
 
289
 
290
  def _next_item_for(state: dict) -> tuple[str, str, str, dict]:
291
+ """Advance `state` to the next unclaimed item and return render strings."""
 
 
 
292
  annotator = state["annotator"]
293
  order = state["order"]
294
  idx = state.get("idx", 0)
295
 
 
 
296
  new_idx, mt = _claim_next(annotator, order, idx)
297
  state["idx"] = new_idx
298
  if mt is None:
299
  state["current"] = None
300
+ return ALL_DONE_VIDEO, "**全部完成**,池中已无剩余任务。", "", state
301
 
302
  state["current"] = mt
303
  state["claimed_n"] = state.get("claimed_n", 0) + 1
 
320
  if not annotator:
321
  return (
322
  state, PLACEHOLDER_VIDEO, "", "",
323
+ "⚠️ 请先输入您的名字。", _stats_md(),
324
  )
 
 
 
325
  order = list(range(len(POOL)))
326
  rng = random.Random(f"{annotator}-{int(time.time())}-{uuid.uuid4().hex}")
327
  rng.shuffle(order)
328
  state = {"annotator": annotator, "order": order, "idx": 0,
329
  "current": None, "submitted_count": 0, "claimed_n": 0}
330
  video_html, meta, prompt, state = _next_item_for(state)
331
+ status = f"✅ 欢迎 `{annotator}`,开始标注吧!"
332
  return state, video_html, meta, prompt, status, _stats_md()
333
 
334
 
 
336
  """Record current claim as submitted, then advance."""
337
  if not state or "annotator" not in state:
338
  return (
339
+ state, PLACEHOLDER_VIDEO, "", "", "", "",
340
+ "⚠️ 请先登录。", _stats_md(),
341
  )
342
  current = state.get("current")
343
  if current is None:
 
344
  return (
345
+ state, ALL_DONE_VIDEO, "**全部完成**", "", "", "",
346
+ "当前没有可提交的任务。", _stats_md(),
347
  )
348
  model, task_id = current
349
  record = {
 
353
  "process_id": PROCESS_ID,
354
  "model": model,
355
  "task_id": task_id,
356
+ "memory_issue": verdict == "",
357
+ "verdict": verdict,
358
  "note": (note or "").strip(),
359
  }
360
  _append_annotation(record)
 
366
  video_html, meta, prompt, state = _next_item_for(state)
367
  return (
368
  state, video_html, meta, prompt,
369
+ "", # reset verdict
370
+ "", # reset note
371
+ f"✅ 已提交第 {state['submitted_count']} 条,下一条 ",
372
  _stats_md(),
373
  )
374
 
 
377
  """Release the current claim back to the pool and advance."""
378
  if not state or "annotator" not in state:
379
  return (
380
+ state, PLACEHOLDER_VIDEO, "", "", "", "",
381
+ "⚠️ 请先登录。", _stats_md(),
382
  )
383
  current = state.get("current")
384
  if current is not None:
 
389
  video_html, meta, prompt, state = _next_item_for(state)
390
  return (
391
  state, video_html, meta, prompt,
392
+ "", "",
393
+ f"⏭️ 已跳过。本次已提交 {state.get('submitted_count', 0)}",
394
  _stats_md(),
395
  )
396
 
 
403
  #prompt_box textarea { height: 480px !important; overflow-y: auto !important; }
404
  """
405
 
406
+ with gr.Blocks(title="MBench-V 标注", theme=gr.themes.Soft(),
407
  css=CUSTOM_CSS) as demo:
408
  gr.Markdown(
409
  """
410
+ # 🎬 MBench-V 视频标注 *该视频是否出现了记忆问题?*
411
 
412
+ 请对照右侧的 5 Prompt 观看左侧视频,然后选择 **是** **否**。
 
413
  """
414
  )
415
 
 
419
 
420
  with gr.Row():
421
  annotator_in = gr.Textbox(
422
+ label="标注员名字", placeholder="例如:alice", scale=4, autofocus=True,
423
  )
424
+ login_btn = gr.Button("开始", variant="primary", scale=1)
425
 
426
+ status_md = gr.Markdown("_尚未开始。_")
427
 
428
+ # ========= 上排:视频Prompt =========
429
  with gr.Row(equal_height=True):
430
  with gr.Column(scale=3):
431
+ video = gr.HTML(value=PLACEHOLDER_VIDEO, label="生成的视频")
432
  with gr.Column(scale=2):
433
  prompt_tb = gr.Textbox(
434
+ label="生成 Prompt(共 5 段)",
435
  lines=22, max_lines=22,
436
  interactive=False, show_copy_button=True,
437
  elem_id="prompt_box",
438
  )
439
 
440
+ # ========= 下排:元信息选项 =========
441
  with gr.Row():
442
  with gr.Column(scale=3):
443
+ meta_md = gr.Markdown("_当前没有加载任务。_")
444
  with gr.Column(scale=2):
445
  verdict = gr.Radio(
446
+ choices=["", ""], value="",
447
+ label="该视频是否出现了记忆问题?",
448
  )
449
+ note = gr.Textbox(label="备注(可选)", lines=2)
450
  with gr.Row():
451
+ submit_btn = gr.Button("✅ 提交并下一条", variant="primary")
452
+ skip_btn = gr.Button("⏭️ 跳过")
453
 
454
  # ========= Wiring =========
455
  login_outputs = [state, video, meta_md, prompt_tb, status_md, stats_md]