adddrett commited on
Commit
fe162f8
·
1 Parent(s): 6e11729
Files changed (1) hide show
  1. app.py +154 -118
app.py CHANGED
@@ -8,7 +8,6 @@ from typing import Dict, List, Optional, Tuple, Any
8
  import json
9
  import os
10
  import base64
11
- import tempfile
12
 
13
  # ============== 全局状态 ==============
14
 
@@ -52,6 +51,15 @@ class AppState:
52
  break
53
 
54
  def navigate(self, direction: int) -> bool:
 
 
 
 
 
 
 
 
 
55
  new_index = self.current_index + direction
56
  if 0 <= new_index < len(self.all_paths):
57
  self.current_index = new_index
@@ -63,28 +71,47 @@ class AppState:
63
  return True
64
  return False
65
 
66
- # 全局状态实例
67
- app_state = AppState()
68
-
69
- # 全局变量存储当前 QA 数据和审核记录
70
- current_qa_data = []
71
- current_reviews_data = {}
72
 
73
 
74
  # ============== UI 更新函数 ==============
75
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  def update_chart_type_dropdown(source: str):
77
  """更新图表类型下拉框"""
78
- app_state.current_source = source
79
  structure = data_manager.get_dataset_structure()
 
80
  chart_types = list(structure.get('sources', {}).get(source, {}).get('chart_types', {}).keys())
 
81
  return gr.Dropdown(choices=chart_types, value=chart_types[0] if chart_types else None)
82
 
83
 
84
  def update_chart_dropdown(source: str, chart_type: str):
85
  """更新图表和模型下拉框"""
86
- app_state.current_source = source
87
- app_state.current_chart_type = chart_type
88
 
89
  charts = data_manager.get_chart_list(source, chart_type)
90
  structure = data_manager.get_dataset_structure()
@@ -98,11 +125,15 @@ def update_chart_dropdown(source: str, chart_type: str):
98
 
99
 
100
  def create_embedded_html(html_content: str, chart_id: str = "") -> str:
101
- """创建嵌入式的 HTML 显示"""
 
 
 
 
102
  if not html_content:
103
  return f"""
104
  <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;
105
- min-height:500px;color:#999;border:2px dashed #ddd;border-radius:12px;background:#fafafa;">
106
  <div style="font-size:48px;margin-bottom:16px;">📭</div>
107
  <div style="font-size:18px;font-weight:500;">暂无图表内容</div>
108
  <div style="font-size:14px;margin-top:8px;">图表 ID: {chart_id or '未知'}</div>
@@ -110,43 +141,54 @@ def create_embedded_html(html_content: str, chart_id: str = "") -> str:
110
  </div>
111
  """
112
 
 
113
  html_bytes = html_content.encode('utf-8')
114
  html_base64 = base64.b64encode(html_bytes).decode('utf-8')
115
 
116
- return f"""
 
117
  <iframe
118
  src="data:text/html;base64,{html_base64}"
119
- style="width:100%;height:650px;border:1px solid #e0e0e0;border-radius:8px;background:#fff;"
120
  sandbox="allow-scripts allow-same-origin"
121
  loading="lazy"
122
  ></iframe>
123
  """
 
 
124
 
125
 
126
  def load_chart_data(source: str, chart_type: str, chart_id: str, model: str):
127
- """加载图表数据"""
128
- global current_qa_data, current_reviews_data
129
 
 
 
 
130
  if not all([source, chart_type, chart_id, model]):
131
- current_qa_data = []
132
- current_reviews_data = {}
133
  return [
134
- create_embedded_html(""),
135
- "### 请在左侧选择图表",
136
- "等待加载数据...",
137
- "请在左侧选择图表",
138
- gr.Radio(choices=[], value=None),
139
- ""
 
 
140
  ]
141
 
142
- app_state.set_position(source, chart_type, chart_id, model)
 
143
 
144
  # 获取图表数据
145
  chart_data = data_manager.get_chart_data(source, chart_type, chart_id)
146
  html_content = chart_data.get('html_content', '')
147
  label_info = chart_data.get('label_info', {})
148
 
 
149
  embedded_html = create_embedded_html(html_content, chart_id)
 
 
150
  debug_info = f"📁 {source}/{chart_type}/{chart_id} | HTML: {len(html_content)} 字符"
151
 
152
  # 格式化标签信息
@@ -168,33 +210,38 @@ def load_chart_data(source: str, chart_type: str, chart_id: str, model: str):
168
 
169
  # 获取 QA 列表
170
  qa_list = data_manager.get_qa_list(source, chart_type, model, chart_id)
171
- current_qa_data = [{"id": qa.id, "question": qa.question, "answer": qa.answer} for qa in qa_list]
172
 
173
  # 获取已有的审核记录
174
- current_reviews_data = {}
175
  for review in data_manager.get_reviews_by_chart(chart_id, model):
176
- current_reviews_data[review['qa_id']] = review
177
 
178
- progress_text = f"当前: {app_state.current_index + 1} / {len(app_state.all_paths)} 个图表"
 
 
 
179
  stats = data_manager.get_review_stats()
180
  status_text = f"已审核: {stats['total']} | ✅正确: {stats['correct']} | ❌错误: {stats['incorrect']} | ✏️需修改: {stats['needs_modification']}"
181
 
 
182
  qa_choices = [f"Q{i+1}: {qa.question[:50]}..." for i, qa in enumerate(qa_list)] if qa_list else []
183
 
184
  return [
185
- embedded_html,
186
- label_text,
187
- status_text,
188
- progress_text,
189
- gr.Radio(choices=qa_choices, value=qa_choices[0] if qa_choices else None),
190
- debug_info
 
 
191
  ]
192
 
193
 
194
  def navigate_prev():
195
  """导航到上一个图表"""
196
- if app_state.navigate(-1):
197
- path = app_state.get_current_path()
198
  if path:
199
  return (
200
  gr.Dropdown(value=path['source']),
@@ -207,8 +254,8 @@ def navigate_prev():
207
 
208
  def navigate_next():
209
  """导航到下一个图表"""
210
- if app_state.navigate(1):
211
- path = app_state.get_current_path()
212
  if path:
213
  return (
214
  gr.Dropdown(value=path['source']),
@@ -219,33 +266,6 @@ def navigate_next():
219
  return [gr.Dropdown(), gr.Dropdown(), gr.Dropdown(), gr.Dropdown()]
220
 
221
 
222
- def on_qa_selected(qa_index_str: str):
223
- """QA 选择器变化时更新审核面板"""
224
- global current_qa_data, current_reviews_data
225
-
226
- if not qa_index_str or not current_qa_data:
227
- return ["", "", gr.Radio(value="pending"), "", "", "", ""]
228
-
229
- try:
230
- index = int(qa_index_str.split(":")[0].replace("Q", "")) - 1
231
- qa = current_qa_data[index]
232
- review = current_reviews_data.get(qa['id'], {})
233
-
234
- return [
235
- qa['id'],
236
- qa['question'],
237
- qa['answer'],
238
- gr.Radio(value=review.get('status', 'pending')),
239
- review.get('issue_type', ''),
240
- review.get('modified_question', ''),
241
- review.get('modified_answer', ''),
242
- review.get('comment', '')
243
- ]
244
- except Exception as e:
245
- print(f"Error in on_qa_selected: {e}")
246
- return ["", "", gr.Radio(value="pending"), "", "", "", ""]
247
-
248
-
249
  def save_review_handler(
250
  qa_id: str,
251
  chart_id: str,
@@ -281,33 +301,17 @@ def save_review_handler(
281
  "reviewer": reviewer
282
  }
283
 
284
- data_manager.save_review(review_data)
285
-
286
- # 更新全局审核数据
287
- global current_reviews_data
288
- current_reviews_data[qa_id] = review_data
289
 
 
290
  stats = data_manager.get_review_stats()
291
  return f"✅ 已保存! 总计: {stats['total']} | ✅正确: {stats['correct']} | ❌错误: {stats['incorrect']} | ✏️需修改: {stats['needs_modification']}"
292
 
293
 
294
  def export_reviews_handler():
295
- """导出审核记录并返回文件路径供下载"""
296
- reviews = data_manager.get_all_reviews()
297
-
298
- if not reviews:
299
- return None, "❌ 暂无审核记录可导出"
300
-
301
- temp_dir = tempfile.gettempdir()
302
- output_path = os.path.join(temp_dir, "reviews_export.json")
303
-
304
- with open(output_path, 'w', encoding='utf-8') as f:
305
- json.dump(reviews, f, ensure_ascii=False, indent=2)
306
-
307
- stats = data_manager.get_review_stats()
308
- summary = f"✅ 导出成功!共 {len(reviews)} 条记录"
309
-
310
- return output_path, summary
311
 
312
 
313
  # ============== 创建 Gradio 界面 ==============
@@ -315,9 +319,10 @@ def export_reviews_handler():
315
  def create_ui():
316
  """创建 Gradio 界面"""
317
 
 
318
  custom_css = """
319
  .chart-container {
320
- min-height: 680px;
321
  }
322
 
323
  .control-panel {
@@ -335,13 +340,6 @@ def create_ui():
335
  border-radius: 4px;
336
  margin-top: 10px;
337
  }
338
-
339
- .export-section {
340
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
341
- padding: 15px;
342
- border-radius: 8px;
343
- margin-top: 15px;
344
- }
345
  """
346
 
347
  with gr.Blocks(
@@ -350,6 +348,10 @@ def create_ui():
350
  css=custom_css
351
  ) as app:
352
 
 
 
 
 
353
  # ==================== 标题栏 ====================
354
  gr.Markdown("""
355
  # 📊 图表问答数据集审核系统
@@ -360,12 +362,14 @@ def create_ui():
360
  # ==================== 顶部状态栏 ====================
361
  with gr.Row():
362
  status_text = gr.Textbox(
 
363
  value="等待加载数据...",
364
  interactive=False,
365
  show_label=False,
366
  scale=2
367
  )
368
  progress_text = gr.Textbox(
 
369
  value="请在左侧选择图表",
370
  interactive=False,
371
  show_label=False,
@@ -375,7 +379,7 @@ def create_ui():
375
  # ==================== 主内容区 ====================
376
  with gr.Row():
377
  # ===== 左侧:导航控制 =====
378
- with gr.Column(scale=1, min_width=280):
379
  gr.Markdown("### 📁 数据选择")
380
 
381
  source_dropdown = gr.Dropdown(
@@ -409,12 +413,9 @@ def create_ui():
409
  prev_btn = gr.Button("⬅️ 上一个")
410
  next_btn = gr.Button("➡️ 下一个")
411
 
412
- # 导出区域
413
- gr.Markdown("### 📥 导出审核结果")
414
- with gr.Column(elem_classes=["export-section"]):
415
- export_btn = gr.Button("📤 生成导出文件", variant="secondary", size="sm")
416
- export_status = gr.Textbox(value="", interactive=False, show_label=False)
417
- download_file = gr.File(label="📥 点击下载", visible=False)
418
 
419
  # 审核人设置
420
  reviewer_input = gr.Textbox(
@@ -425,6 +426,7 @@ def create_ui():
425
 
426
  # 调试信息
427
  debug_info = gr.Textbox(
 
428
  value="",
429
  interactive=False,
430
  show_label=False,
@@ -432,19 +434,25 @@ def create_ui():
432
  )
433
 
434
  # ===== 中间:图表展示 =====
435
- with gr.Column(scale=3, min_width=500):
436
  gr.Markdown("### 📈 图表展示")
437
 
 
438
  html_display = gr.HTML(
439
- value="<div style='text-align:center;padding:100px;color:#999;'>请选择图表</div>",
440
  elem_classes=["chart-container"]
441
  )
442
 
443
  # ===== 右侧:标签信息和 QA 审核 =====
444
  with gr.Column(scale=2, min_width=400):
 
445
  gr.Markdown("### 📝 图表标签")
446
- label_display = gr.Markdown(value="暂无信息", elem_classes=["control-panel"])
 
 
 
447
 
 
448
  gr.Markdown("### ❓ 问答审核")
449
 
450
  # 当前选中的 QA 信息(隐藏)
@@ -530,7 +538,7 @@ def create_ui():
530
 
531
  # 保存按钮
532
  save_btn = gr.Button("💾 保存审核结果", variant="primary")
533
- save_result = gr.Textbox(value="", visible=False)
534
 
535
  # ==================== 事件绑定 ====================
536
 
@@ -564,8 +572,8 @@ def create_ui():
564
  fn=load_chart_data,
565
  inputs=[source_dropdown, chart_type_dropdown, chart_dropdown, model_dropdown],
566
  outputs=[
567
- html_display, label_display, status_text, progress_text,
568
- qa_selector, debug_info
569
  ]
570
  )
571
 
@@ -573,19 +581,47 @@ def create_ui():
573
  fn=load_chart_data,
574
  inputs=[source_dropdown, chart_type_dropdown, chart_dropdown, model_dropdown],
575
  outputs=[
576
- html_display, label_display, status_text, progress_text,
577
- qa_selector, debug_info
578
  ]
579
  )
580
 
581
  # QA 选择器变化 -> 更新审核面板
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
582
  qa_selector.change(
583
  fn=on_qa_selected,
584
- inputs=[qa_selector],
585
  outputs=[
586
  current_qa_id, qa_question_display, qa_answer_display,
587
- status_radio, issue_type_dropdown, modified_question,
588
- modified_answer, comment_textbox
589
  ]
590
  )
591
 
@@ -624,13 +660,13 @@ def create_ui():
624
  outputs=[save_result]
625
  )
626
 
627
- # 导出审核记录
628
  export_btn.click(
629
  fn=export_reviews_handler,
630
- outputs=[download_file, export_status]
631
  ).then(
632
- fn=lambda: gr.File(visible=True),
633
- outputs=[download_file]
634
  )
635
 
636
  return app
@@ -643,5 +679,5 @@ if __name__ == "__main__":
643
  app.launch(
644
  server_name="0.0.0.0",
645
  server_port=7860,
646
- share=False
647
  )
 
8
  import json
9
  import os
10
  import base64
 
11
 
12
  # ============== 全局状态 ==============
13
 
 
51
  break
52
 
53
  def navigate(self, direction: int) -> bool:
54
+ """
55
+ 导航到上一个或下一个图表
56
+
57
+ Args:
58
+ direction: 1 表示下一个,-1 表示上一个
59
+
60
+ Returns:
61
+ 是否成功导航
62
+ """
63
  new_index = self.current_index + direction
64
  if 0 <= new_index < len(self.all_paths):
65
  self.current_index = new_index
 
71
  return True
72
  return False
73
 
74
+ state = AppState()
 
 
 
 
 
75
 
76
 
77
  # ============== UI 更新函数 ==============
78
 
79
+ def get_dataset_choices() -> Tuple[List[str], List[str], List[str], List[str]]:
80
+ """获取数据集的选择项"""
81
+ structure = data_manager.get_dataset_structure()
82
+
83
+ sources = list(structure.get('sources', {}).keys())
84
+
85
+ chart_types = []
86
+ charts = []
87
+ models = []
88
+
89
+ if state.current_source:
90
+ source_data = structure['sources'].get(state.current_source, {})
91
+ chart_types = list(source_data.get('chart_types', {}).keys())
92
+
93
+ if state.current_chart_type:
94
+ charts = data_manager.get_chart_list(state.current_source, state.current_chart_type)
95
+ ct_data = source_data.get('chart_types', {}).get(state.current_chart_type, {})
96
+ models = ct_data.get('models', [])
97
+
98
+ return sources, chart_types, charts, models
99
+
100
+
101
  def update_chart_type_dropdown(source: str):
102
  """更新图表类型下拉框"""
103
+ state.current_source = source
104
  structure = data_manager.get_dataset_structure()
105
+
106
  chart_types = list(structure.get('sources', {}).get(source, {}).get('chart_types', {}).keys())
107
+
108
  return gr.Dropdown(choices=chart_types, value=chart_types[0] if chart_types else None)
109
 
110
 
111
  def update_chart_dropdown(source: str, chart_type: str):
112
  """更新图表和模型下拉框"""
113
+ state.current_source = source
114
+ state.current_chart_type = chart_type
115
 
116
  charts = data_manager.get_chart_list(source, chart_type)
117
  structure = data_manager.get_dataset_structure()
 
125
 
126
 
127
  def create_embedded_html(html_content: str, chart_id: str = "") -> str:
128
+ """
129
+ 创建嵌入式的 HTML 显示
130
+
131
+ 使用 data URI 方式嵌入 HTML 内容到 iframe 中
132
+ """
133
  if not html_content:
134
  return f"""
135
  <div style="display:flex;flex-direction:column;align-items:center;justify-content:center;
136
+ min-height:400px;color:#999;border:2px dashed #ddd;border-radius:12px;background:#fafafa;">
137
  <div style="font-size:48px;margin-bottom:16px;">📭</div>
138
  <div style="font-size:18px;font-weight:500;">暂无图表内容</div>
139
  <div style="font-size:14px;margin-top:8px;">图表 ID: {chart_id or '未知'}</div>
 
141
  </div>
142
  """
143
 
144
+ # 使用 base64 编码 HTML 内容,避免引号转义问题
145
  html_bytes = html_content.encode('utf-8')
146
  html_base64 = base64.b64encode(html_bytes).decode('utf-8')
147
 
148
+ # 使用 data URI
149
+ iframe_html = f"""
150
  <iframe
151
  src="data:text/html;base64,{html_base64}"
152
+ style="width:100%;height:500px;border:1px solid #e0e0e0;border-radius:8px;background:#fff;"
153
  sandbox="allow-scripts allow-same-origin"
154
  loading="lazy"
155
  ></iframe>
156
  """
157
+
158
+ return iframe_html
159
 
160
 
161
  def load_chart_data(source: str, chart_type: str, chart_id: str, model: str):
162
+ """
163
+ 加载图表数据并返回所有 UI 更新
164
 
165
+ Returns:
166
+ 包含所有 UI 组件更新值的元组
167
+ """
168
  if not all([source, chart_type, chart_id, model]):
 
 
169
  return [
170
+ create_embedded_html(""), # html_display
171
+ "### 请在左侧选择图表", # label_info
172
+ "[]", # qa_data (JSON string)
173
+ "等待加载数据...", # status_text
174
+ "请在左侧选择图表", # progress_text
175
+ "{}", # current_qa_reviews (JSON string)
176
+ gr.Radio(choices=[], value=None), # qa_selector
177
+ "" # debug_info
178
  ]
179
 
180
+ # 更新状态
181
+ state.set_position(source, chart_type, chart_id, model)
182
 
183
  # 获取图表数据
184
  chart_data = data_manager.get_chart_data(source, chart_type, chart_id)
185
  html_content = chart_data.get('html_content', '')
186
  label_info = chart_data.get('label_info', {})
187
 
188
+ # 创建嵌入式 HTML
189
  embedded_html = create_embedded_html(html_content, chart_id)
190
+
191
+ # 调试信息
192
  debug_info = f"📁 {source}/{chart_type}/{chart_id} | HTML: {len(html_content)} 字符"
193
 
194
  # 格式化标签信息
 
210
 
211
  # 获取 QA 列表
212
  qa_list = data_manager.get_qa_list(source, chart_type, model, chart_id)
 
213
 
214
  # 获取已有的审核记录
215
+ existing_reviews = {}
216
  for review in data_manager.get_reviews_by_chart(chart_id, model):
217
+ existing_reviews[review['qa_id']] = review
218
 
219
+ # 更新进度文本
220
+ progress_text = f"当前: {state.current_index + 1} / {len(state.all_paths)} 个图表"
221
+
222
+ # 状态文本
223
  stats = data_manager.get_review_stats()
224
  status_text = f"已审核: {stats['total']} | ✅正确: {stats['correct']} | ❌错误: {stats['incorrect']} | ✏️需修改: {stats['needs_modification']}"
225
 
226
+ # QA 选择器选项
227
  qa_choices = [f"Q{i+1}: {qa.question[:50]}..." for i, qa in enumerate(qa_list)] if qa_list else []
228
 
229
  return [
230
+ embedded_html, # html_display
231
+ label_text, # label_info
232
+ json.dumps([{"id": qa.id, "question": qa.question, "answer": qa.answer} for qa in qa_list]), # qa_data (JSON string)
233
+ status_text, # status_text
234
+ progress_text, # progress_text
235
+ json.dumps(existing_reviews), # current_qa_reviews (JSON string)
236
+ gr.Radio(choices=qa_choices, value=qa_choices[0] if qa_choices else None), # qa_selector
237
+ debug_info # debug_info
238
  ]
239
 
240
 
241
  def navigate_prev():
242
  """导航到上一个图表"""
243
+ if state.navigate(-1):
244
+ path = state.get_current_path()
245
  if path:
246
  return (
247
  gr.Dropdown(value=path['source']),
 
254
 
255
  def navigate_next():
256
  """导航到下一个图表"""
257
+ if state.navigate(1):
258
+ path = state.get_current_path()
259
  if path:
260
  return (
261
  gr.Dropdown(value=path['source']),
 
266
  return [gr.Dropdown(), gr.Dropdown(), gr.Dropdown(), gr.Dropdown()]
267
 
268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
269
  def save_review_handler(
270
  qa_id: str,
271
  chart_id: str,
 
301
  "reviewer": reviewer
302
  }
303
 
304
+ result = data_manager.save_review(review_data)
 
 
 
 
305
 
306
+ # 返回更新后的统计
307
  stats = data_manager.get_review_stats()
308
  return f"✅ 已保存! 总计: {stats['total']} | ✅正确: {stats['correct']} | ❌错误: {stats['incorrect']} | ✏️需修改: {stats['needs_modification']}"
309
 
310
 
311
  def export_reviews_handler():
312
+ """导出审核记录"""
313
+ output_path = data_manager.export_reviews("./reviews_export.json")
314
+ return f"✅ 审核记录已导出至: {output_path}"
 
 
 
 
 
 
 
 
 
 
 
 
 
315
 
316
 
317
  # ============== 创建 Gradio 界面 ==============
 
319
  def create_ui():
320
  """创建 Gradio 界面"""
321
 
322
+ # 自定义 CSS
323
  custom_css = """
324
  .chart-container {
325
+ min-height: 520px;
326
  }
327
 
328
  .control-panel {
 
340
  border-radius: 4px;
341
  margin-top: 10px;
342
  }
 
 
 
 
 
 
 
343
  """
344
 
345
  with gr.Blocks(
 
348
  css=custom_css
349
  ) as app:
350
 
351
+ # 隐藏的状态存储(使用 JSON 字符串)
352
+ qa_data_json = gr.State(value="[]")
353
+ current_reviews_json = gr.State(value="{}")
354
+
355
  # ==================== 标题栏 ====================
356
  gr.Markdown("""
357
  # 📊 图表问答数据集审核系统
 
362
  # ==================== 顶部状态栏 ====================
363
  with gr.Row():
364
  status_text = gr.Textbox(
365
+ label="审核统计",
366
  value="等待加载数据...",
367
  interactive=False,
368
  show_label=False,
369
  scale=2
370
  )
371
  progress_text = gr.Textbox(
372
+ label="进度",
373
  value="请在左侧选择图表",
374
  interactive=False,
375
  show_label=False,
 
379
  # ==================== 主内容区 ====================
380
  with gr.Row():
381
  # ===== 左侧:导航控制 =====
382
+ with gr.Column(scale=1, min_width=250):
383
  gr.Markdown("### 📁 数据选择")
384
 
385
  source_dropdown = gr.Dropdown(
 
413
  prev_btn = gr.Button("⬅️ 上一个")
414
  next_btn = gr.Button("➡️ 下一个")
415
 
416
+ # 导出按钮
417
+ export_btn = gr.Button("📥 导出审核记录", variant="secondary")
418
+ export_result = gr.Textbox(label="", visible=False)
 
 
 
419
 
420
  # 审核人设置
421
  reviewer_input = gr.Textbox(
 
426
 
427
  # 调试信息
428
  debug_info = gr.Textbox(
429
+ label="调试信息",
430
  value="",
431
  interactive=False,
432
  show_label=False,
 
434
  )
435
 
436
  # ===== 中间:图表展示 =====
437
+ with gr.Column(scale=2, min_width=400):
438
  gr.Markdown("### 📈 图表展示")
439
 
440
+ # HTML 图表展示(使用 iframe)
441
  html_display = gr.HTML(
442
+ value="<div style='text-align:center;padding:50px;color:#999;'>请选择图表</div>",
443
  elem_classes=["chart-container"]
444
  )
445
 
446
  # ===== 右侧:标签信息和 QA 审核 =====
447
  with gr.Column(scale=2, min_width=400):
448
+ # 标签信息
449
  gr.Markdown("### 📝 图表标签")
450
+ label_display = gr.Markdown(
451
+ value="暂无信息",
452
+ elem_classes=["control-panel"]
453
+ )
454
 
455
+ # QA 审核区
456
  gr.Markdown("### ❓ 问答审核")
457
 
458
  # 当前选中的 QA 信息(隐藏)
 
538
 
539
  # 保存按钮
540
  save_btn = gr.Button("💾 保存审核结果", variant="primary")
541
+ save_result = gr.Textbox(label="", visible=False)
542
 
543
  # ==================== 事件绑定 ====================
544
 
 
572
  fn=load_chart_data,
573
  inputs=[source_dropdown, chart_type_dropdown, chart_dropdown, model_dropdown],
574
  outputs=[
575
+ html_display, label_display, qa_data_json, status_text, progress_text,
576
+ current_reviews_json, qa_selector, debug_info
577
  ]
578
  )
579
 
 
581
  fn=load_chart_data,
582
  inputs=[source_dropdown, chart_type_dropdown, chart_dropdown, model_dropdown],
583
  outputs=[
584
+ html_display, label_display, qa_data_json, status_text, progress_text,
585
+ current_reviews_json, qa_selector, debug_info
586
  ]
587
  )
588
 
589
  # QA 选择器变化 -> 更新审核面板
590
+ def on_qa_selected(qa_index_str, qa_json, reviews_json):
591
+ if not qa_index_str or not qa_json:
592
+ return ["", "", "", gr.Radio(value="pending"), "", "", "", ""]
593
+
594
+ try:
595
+ qa_list = json.loads(qa_json)
596
+ reviews = json.loads(reviews_json)
597
+
598
+ # 解析索引
599
+ index = int(qa_index_str.split(":")[0].replace("Q", "")) - 1
600
+ qa = qa_list[index]
601
+
602
+ # 检查是否有现有审核
603
+ review = reviews.get(qa['id'], {})
604
+
605
+ return [
606
+ qa['id'], # current_qa_id
607
+ qa['question'], # qa_question_display
608
+ qa['answer'], # qa_answer_display
609
+ gr.Radio(value=review.get('status', 'pending')), # status_radio
610
+ review.get('issue_type', ''), # issue_type_dropdown
611
+ review.get('modified_question', ''), # modified_question
612
+ review.get('modified_answer', ''), # modified_answer
613
+ review.get('comment', '') # comment_textbox
614
+ ]
615
+ except Exception as e:
616
+ print(f"Error in on_qa_selected: {e}")
617
+ return ["", "", "", gr.Radio(value="pending"), "", "", "", ""]
618
+
619
  qa_selector.change(
620
  fn=on_qa_selected,
621
+ inputs=[qa_selector, qa_data_json, current_reviews_json],
622
  outputs=[
623
  current_qa_id, qa_question_display, qa_answer_display,
624
+ status_radio, issue_type_dropdown, modified_question, modified_answer, comment_textbox
 
625
  ]
626
  )
627
 
 
660
  outputs=[save_result]
661
  )
662
 
663
+ # 导出
664
  export_btn.click(
665
  fn=export_reviews_handler,
666
+ outputs=[export_result]
667
  ).then(
668
+ fn=lambda: gr.Textbox(visible=True),
669
+ outputs=[export_result]
670
  )
671
 
672
  return app
 
679
  app.launch(
680
  server_name="0.0.0.0",
681
  server_port=7860,
682
+ share=True
683
  )