adddrett commited on
Commit
8dd9716
·
1 Parent(s): 585bcee
Files changed (1) hide show
  1. app.py +273 -155
app.py CHANGED
@@ -1,223 +1,341 @@
1
  """
2
- 图表问答数据集审核系统 - Gradio 5.x 稳定版
3
  """
4
  import gradio as gr
5
- from data_manager import data_manager # 假设你已经实例化了 data_manager
 
6
  import json
 
7
  import base64
8
- from typing import Dict, List, Optional
9
 
10
- # ============== 全局状态管理 ==============
11
 
12
  class AppState:
13
  def __init__(self):
14
- self.current_source = ""
15
- self.current_chart_type = ""
16
- self.current_chart_id = ""
17
- self.current_model = ""
18
- self.all_paths = []
19
- self.current_index = -1
20
  self.refresh_paths()
21
 
22
  def refresh_paths(self):
23
  self.all_paths = data_manager.get_all_chart_paths()
24
 
25
- def set_position(self, source, chart_type, chart_id, model):
 
 
 
 
 
26
  self.current_source = source
27
  self.current_chart_type = chart_type
28
  self.current_chart_id = chart_id
29
  self.current_model = model
30
  for i, path in enumerate(self.all_paths):
31
- if (path['source'] == source and path['chart_type'] == chart_type and
32
- path['chart_id'] == chart_id and path['model'] == model):
 
 
33
  self.current_index = i
34
  break
35
 
36
- def navigate(self, direction):
37
  new_index = self.current_index + direction
38
  if 0 <= new_index < len(self.all_paths):
39
  self.current_index = new_index
40
  path = self.all_paths[new_index]
41
- return path['source'], path['chart_type'], path['chart_id'], path['model']
42
- return None
 
 
 
 
43
 
44
  state = AppState()
45
 
46
- # ============== 辅助逻辑 ==============
 
 
47
 
48
- def create_embedded_html(html_content: str) -> str:
49
- if not html_content:
50
- return '<div style="height:600px;display:flex;align-items:center;justify-content:center;background:#eee;">请选择图表进行加载</div>'
51
- # 使用 Base64 嵌入以避免跨域和字符编码问题
52
- html_base64 = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
53
- return f'<iframe src="data:text/html;base64,{html_base64}" style="width:100%;height:700px;border:none;background:white;"></iframe>'
54
 
55
- # ============== UI 事件处理 ==============
 
 
 
56
 
57
- def on_source_change(source):
 
58
  structure = data_manager.get_dataset_structure()
59
- types = list(structure.get('sources', {}).get(source, {}).get('chart_types', {}).keys())
60
- return gr.update(choices=types, value=types[0] if types else None)
61
 
62
- def on_type_change(source, chart_type):
 
 
63
  charts = data_manager.get_chart_list(source, chart_type)
64
  structure = data_manager.get_dataset_structure()
65
- models = structure.get('sources', {}).get(source, {}).get('chart_types', {}).get(chart_type, {}).get('models', [])
 
66
  return (
67
- gr.update(choices=charts, value=charts[0] if charts else None),
68
- gr.update(choices=models, value=models[0] if models else None)
69
  )
70
 
71
- def load_data(source, chart_type, chart_id, model):
 
 
 
 
 
 
 
 
 
 
 
 
72
  if not all([source, chart_type, chart_id, model]):
73
- return ["", "", "[]", "等待选择...", "0/0", "{}", gr.update(choices=[]), "未选择"]
 
 
 
 
 
 
 
 
 
74
 
75
  state.set_position(source, chart_type, chart_id, model)
76
  chart_data = data_manager.get_chart_data(source, chart_type, chart_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  qa_list = data_manager.get_qa_list(source, chart_type, model, chart_id)
78
- reviews = {r['qa_id']: r for r in data_manager.get_reviews_by_chart(chart_id, model)}
 
 
 
 
 
 
79
  stats = data_manager.get_review_stats()
 
 
80
 
81
- # 构造返回值
82
- html_frame = create_embedded_html(chart_data.get('html_content', ''))
83
- label_md = "\n".join([f"- **{k}**: {v}" for k, v in chart_data.get('label_info', {}).items()])
84
- qa_json = json.dumps([{"id": q.id, "question": q.question, "answer": q.answer} for q in qa_list])
85
- stats_msg = f"✅{stats['correct']} | ❌{stats['incorrect']} | 总{stats['total']}"
86
- progress_msg = f"{state.current_index + 1} / {len(state.all_paths)}"
87
- qa_choices = [f"Q{i+1}: {q.question[:30]}..." for i, q in enumerate(qa_list)]
88
 
89
- return [
90
- html_frame, label_md, qa_json, stats_msg, progress_msg,
91
- json.dumps(reviews), gr.update(choices=qa_choices, value=qa_choices[0] if qa_choices else None),
92
- f"{source}/{chart_type}/{chart_id}"
93
- ]
 
 
 
 
 
94
 
95
- # ============== 界面构建 ==============
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
96
 
97
  def create_ui():
98
- with gr.Blocks(title="图表 QA 审核系统", theme=gr.themes.Default()) as app:
99
- # 隐藏状态存储
100
- qa_store = gr.State("[]")
101
- review_store = gr.State("{}")
 
 
 
102
 
103
- gr.Markdown("# 📊 图表问答数据集审核")
 
 
 
104
 
 
105
  with gr.Row():
106
- with gr.Column(scale=3):
107
- with gr.Row():
108
- source_drop = gr.Dropdown(label="数据源", choices=[], allow_custom_value=True)
109
- type_drop = gr.Dropdown(label="型", choices=[], allow_custom_value=True)
110
- id_drop = gr.Dropdown(label="图表 ID", choices=[], allow_custom_value=True)
111
- model_drop = gr.Dropdown(label="模型", choices=[], allow_custom_value=True)
112
-
113
- html_view = gr.HTML()
114
- path_display = gr.Text(label="当前路径", interactive=False)
115
-
116
- with gr.Column(scale=1):
117
- stats_view = gr.Textbox(label="审核统计", interactive=False)
118
- progress_view = gr.Textbox(label="总进度", interactive=False)
 
 
 
 
 
 
 
 
 
 
119
 
120
- with gr.Accordion("图表元数据", open=False):
121
- meta_view = gr.Markdown()
122
 
123
- gr.Markdown("### 审核区")
124
- qa_select = gr.Radio(label="选择", choices=[])
125
- curr_qa_id = gr.Text(visible=False)
126
- q_text = gr.Textbox(label="问题", interactive=False, lines=2)
127
- a_text = gr.Textbox(label="答案", interactive=False)
128
 
 
129
  status_radio = gr.Radio(
130
- label="判定",
131
- choices=[("正确", "correct"), ("错误", "incorrect"), ("改后通过", "modified")],
132
- value="correct"
 
 
 
 
 
 
133
  )
134
- issue_type = gr.Dropdown(label="错误类型", choices=["无", "答案错误", "问题模糊", "图表读取失败"])
135
- mod_q = gr.Textbox(label="修改问题(选填)")
136
- mod_a = gr.Textbox(label="修改答案(选填)")
137
- comment = gr.Textbox(label="备注")
138
 
139
  save_btn = gr.Button("💾 保存审核", variant="primary")
140
- with gr.Row():
141
- prev_btn = gr.Button("⬅️ 上一个")
142
- next_btn = gr.Button("➡️ 下一个")
143
-
144
- export_btn = gr.Button("📦 导出 JSON")
145
- download_file = gr.File(label="下载地址", visible=False)
146
-
147
- # --- 交互绑定 ---
148
 
149
- # 初始化
150
- def init_ui():
151
- structure = data_manager.get_dataset_structure()
152
- sources = list(structure.get('sources', {}).keys())
153
- return gr.update(choices=sources, value=sources[0] if sources else None)
154
 
155
- app.load(init_ui, outputs=[source_drop])
 
156
 
157
- # 联动逻辑
158
- source_drop.change(on_source_change, inputs=[source_drop], outputs=[type_drop])
159
- type_drop.change(on_type_change, inputs=[source_drop, type_drop], outputs=[id_drop, model_drop])
 
 
 
 
 
 
 
160
 
161
- # 加载数据
162
- load_inputs = [source_drop, type_drop, id_drop, model_drop]
163
- load_outputs = [html_view, meta_view, qa_store, stats_view, progress_view, review_store, qa_select, path_display]
164
- id_drop.change(load_data, inputs=load_inputs, outputs=load_outputs)
165
- model_drop.change(load_data, inputs=load_inputs, outputs=load_outputs)
166
-
167
- # 题目切换
168
- def switch_qa(sel, qa_json, rev_json):
169
- if not sel or qa_json == "[]": return [""] * 8
170
- qas = json.loads(qa_json)
171
- revs = json.loads(rev_json)
172
- idx = int(sel.split(":")[0][1:]) - 1
173
- curr = qas[idx]
174
- r = revs.get(curr['id'], {})
175
- return [
176
- curr['id'], curr['question'], curr['answer'],
177
- r.get('status', 'correct'), r.get('issue_type', '无'),
178
- r.get('modified_question', ''), r.get('modified_answer', ''),
179
- r.get('comment', '')
180
- ]
181
-
182
- qa_select.change(
183
- switch_qa,
184
- inputs=[qa_select, qa_store, review_store],
185
- outputs=[curr_qa_id, q_text, a_text, status_radio, issue_type, mod_q, mod_a, comment]
186
- )
187
-
188
- # 导航
189
- def nav(direction):
190
- res = state.navigate(direction)
191
- if res:
192
- return [gr.update(value=v) for v in res]
193
- return [gr.update() for _ in range(4)]
194
-
195
- prev_btn.click(lambda: nav(-1), outputs=load_inputs)
196
- next_btn.click(lambda: nav(1), outputs=load_inputs)
197
-
198
- # 保存
199
- def save_handler(q_id, c_id, src, tp, mdl, o_q, o_a, stat, m_q, m_a, it, cmt):
200
- data_manager.save_review({
201
- "qa_id": q_id, "chart_id": c_id, "source": src, "chart_type": tp, "model": mdl,
202
- "original_question": o_q, "original_answer": o_a, "status": stat,
203
- "modified_question": m_q, "modified_answer": m_a, "issue_type": it, "comment": cmt
204
- })
205
- return "已保存"
206
-
207
  save_btn.click(
208
- save_handler,
209
- inputs=[curr_qa_id, id_drop, source_drop, type_drop, model_drop, q_text, a_text, status_radio, mod_q, mod_a, issue_type, comment],
210
- outputs=[gr.Textbox(visible=False)] # 仅触发
211
- )
212
 
213
- export_btn.click(
214
- fn=lambda: gr.update(value=data_manager.export_reviews(), visible=True),
215
- outputs=[download_file]
216
- )
217
-
218
- return app # 必须要 return app
219
 
220
  if __name__ == "__main__":
221
- demo = create_ui()
222
- # show_api=False 是解决那个 TypeError 的关键
223
- demo.launch(server_name="0.0.0.0", server_port=7860, show_api=False)
 
1
  """
2
+ 图表问答数据集审核系统 - Gradio 5.x 应用
3
  """
4
  import gradio as gr
5
+ from data_manager import DataManager, data_manager
6
+ from typing import Dict, List, Optional, Tuple, Any
7
  import json
8
+ import os
9
  import base64
10
+ import tempfile
11
 
12
+ # ============== 全局状态 ==============
13
 
14
  class AppState:
15
  def __init__(self):
16
+ self.current_source: str = ""
17
+ self.current_chart_type: str = ""
18
+ self.current_chart_id: str = ""
19
+ self.current_model: str = ""
20
+ self.all_paths: List[Dict] = []
21
+ self.current_index: int = -1
22
  self.refresh_paths()
23
 
24
  def refresh_paths(self):
25
  self.all_paths = data_manager.get_all_chart_paths()
26
 
27
+ def get_current_path(self) -> Optional[Dict]:
28
+ if 0 <= self.current_index < len(self.all_paths):
29
+ return self.all_paths[self.current_index]
30
+ return None
31
+
32
+ def set_position(self, source: str, chart_type: str, chart_id: str, model: str):
33
  self.current_source = source
34
  self.current_chart_type = chart_type
35
  self.current_chart_id = chart_id
36
  self.current_model = model
37
  for i, path in enumerate(self.all_paths):
38
+ if (path['source'] == source and
39
+ path['chart_type'] == chart_type and
40
+ path['chart_id'] == chart_id and
41
+ path['model'] == model):
42
  self.current_index = i
43
  break
44
 
45
+ def navigate(self, direction: int) -> bool:
46
  new_index = self.current_index + direction
47
  if 0 <= new_index < len(self.all_paths):
48
  self.current_index = new_index
49
  path = self.all_paths[new_index]
50
+ self.current_source = path['source']
51
+ self.current_chart_type = path['chart_type']
52
+ self.current_chart_id = path['chart_id']
53
+ self.current_model = path['model']
54
+ return True
55
+ return False
56
 
57
  state = AppState()
58
 
59
+ # 当前 QA 数据缓存(使用简单全局变量,避免 gr.State 的问题)
60
+ _current_qa_list: List = []
61
+ _current_reviews: Dict = {}
62
 
63
+ # ============== UI 更新函数 ==============
 
 
 
 
 
64
 
65
+ def init_dataset():
66
+ structure = data_manager.get_dataset_structure()
67
+ sources = list(structure.get('sources', {}).keys())
68
+ return gr.Dropdown(choices=sources, value=sources[0] if sources else None)
69
 
70
+ def update_chart_type_dropdown(source: str):
71
+ state.current_source = source
72
  structure = data_manager.get_dataset_structure()
73
+ chart_types = list(structure.get('sources', {}).get(source, {}).get('chart_types', {}).keys())
74
+ return gr.Dropdown(choices=chart_types, value=chart_types[0] if chart_types else None)
75
 
76
+ def update_chart_dropdown(source: str, chart_type: str):
77
+ state.current_source = source
78
+ state.current_chart_type = chart_type
79
  charts = data_manager.get_chart_list(source, chart_type)
80
  structure = data_manager.get_dataset_structure()
81
+ ct_data = structure.get('sources', {}).get(source, {}).get('chart_types', {}).get(chart_type, {})
82
+ models = ct_data.get('models', [])
83
  return (
84
+ gr.Dropdown(choices=charts, value=charts[0] if charts else None),
85
+ gr.Dropdown(choices=models, value=models[0] if models else None)
86
  )
87
 
88
+ def create_embedded_html(html_content: str, chart_id: str = "") -> str:
89
+ if not html_content:
90
+ return f'''<div style="display:flex;flex-direction:column;min-height:750px;align-items:center;justify-content:center;background:#fafafa;border:2px dashed #ddd;border-radius:12px;">
91
+ <div style="font-size:48px;margin-bottom:16px;">📭</div>
92
+ <div style="font-size:18px;color:#666;">暂无图表内容</div>
93
+ <div style="font-size:14px;color:#999;margin-top:8px;">ID: {chart_id}</div>
94
+ </div>'''
95
+ html_base64 = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
96
+ return f'<iframe src="data:text/html;base64,{html_base64}" style="width:100%;height:750px;border:1px solid #e0e0e0;border-radius:8px;background:#fff;" sandbox="allow-scripts allow-same-origin"></iframe>'
97
+
98
+ def load_chart_data(source: str, chart_type: str, chart_id: str, model: str):
99
+ global _current_qa_list, _current_reviews
100
+
101
  if not all([source, chart_type, chart_id, model]):
102
+ _current_qa_list = []
103
+ _current_reviews = {}
104
+ return (
105
+ create_embedded_html(""),
106
+ "### 请选择图表",
107
+ "等待加载...",
108
+ "请选择图表",
109
+ gr.Radio(choices=[], value=None),
110
+ ""
111
+ )
112
 
113
  state.set_position(source, chart_type, chart_id, model)
114
  chart_data = data_manager.get_chart_data(source, chart_type, chart_id)
115
+ html_content = chart_data.get('html_content', '')
116
+ label_info = chart_data.get('label_info', {})
117
+
118
+ embedded_html = create_embedded_html(html_content, chart_id)
119
+
120
+ # 格式化标签
121
+ if label_info:
122
+ label_lines = ["### 📝 图表信息", ""]
123
+ for k, v in label_info.items():
124
+ label_lines.append(f"**{k}**: {v}")
125
+ label_text = "\n".join(label_lines)
126
+ else:
127
+ label_text = "### ⚠️ 暂无标签信息"
128
+
129
+ # 获取 QA 数据
130
  qa_list = data_manager.get_qa_list(source, chart_type, model, chart_id)
131
+ _current_qa_list = [{"id": qa.id, "question": qa.question, "answer": qa.answer} for qa in qa_list]
132
+
133
+ # 获取审核记录
134
+ reviews = data_manager.get_reviews_by_chart(chart_id, model)
135
+ _current_reviews = {r['qa_id']: r for r in reviews}
136
+
137
+ # 统计
138
  stats = data_manager.get_review_stats()
139
+ status_msg = f"已审核: {stats['total']} | ✅正确: {stats['correct']} | ❌错误: {stats['incorrect']} | ✏️需修改: {stats['needs_modification']}"
140
+ progress_msg = f"进度: {state.current_index + 1} / {len(state.all_paths)}"
141
 
142
+ qa_choices = [f"Q{i+1}: {qa.question[:40]}..." for i, qa in enumerate(qa_list)]
 
 
 
 
 
 
143
 
144
+ debug_info = f"📁 {source}/{chart_type}/{chart_id} | HTML: {len(html_content)} 字符"
145
+
146
+ return (
147
+ embedded_html,
148
+ label_text,
149
+ status_msg,
150
+ progress_msg,
151
+ gr.Radio(choices=qa_choices, value=qa_choices[0] if qa_choices else None),
152
+ debug_info
153
+ )
154
 
155
+ def on_qa_select(qa_index_str: str):
156
+ """选择 QA 后更新审核面板"""
157
+ global _current_qa_list, _current_reviews
158
+
159
+ if not qa_index_str or not _current_qa_list:
160
+ return ("", "", "", "correct", "", "", "", "")
161
+
162
+ try:
163
+ idx = int(qa_index_str.split(":")[0].replace("Q", "")) - 1
164
+ qa = _current_qa_list[idx]
165
+ review = _current_reviews.get(qa['id'], {})
166
+
167
+ return (
168
+ qa['id'],
169
+ qa['question'],
170
+ qa['answer'],
171
+ review.get('status', 'correct'),
172
+ review.get('issue_type', ''),
173
+ review.get('modified_question', ''),
174
+ review.get('modified_answer', ''),
175
+ review.get('comment', '')
176
+ )
177
+ except Exception as e:
178
+ print(f"Error: {e}")
179
+ return ("", "", "", "correct", "", "", "", "")
180
+
181
+ def navigate_prev():
182
+ if state.navigate(-1):
183
+ path = state.get_current_path()
184
+ if path:
185
+ return (
186
+ gr.Dropdown(value=path['source']),
187
+ gr.Dropdown(value=path['chart_type']),
188
+ gr.Dropdown(value=path['chart_id']),
189
+ gr.Dropdown(value=path['model'])
190
+ )
191
+ return (gr.Dropdown(), gr.Dropdown(), gr.Dropdown(), gr.Dropdown())
192
+
193
+ def navigate_next():
194
+ if state.navigate(1):
195
+ path = state.get_current_path()
196
+ if path:
197
+ return (
198
+ gr.Dropdown(value=path['source']),
199
+ gr.Dropdown(value=path['chart_type']),
200
+ gr.Dropdown(value=path['chart_id']),
201
+ gr.Dropdown(value=path['model'])
202
+ )
203
+ return (gr.Dropdown(), gr.Dropdown(), gr.Dropdown(), gr.Dropdown())
204
+
205
+ def save_review_handler(qa_id, chart_id, source, chart_type, model, orig_q, orig_a, status, mod_q, mod_a, issue_type, comment, reviewer):
206
+ if not qa_id:
207
+ return "❌ 请先选择问答对"
208
+
209
+ data_manager.save_review({
210
+ "qa_id": qa_id,
211
+ "chart_id": chart_id,
212
+ "source": source,
213
+ "chart_type": chart_type,
214
+ "model": model,
215
+ "original_question": orig_q,
216
+ "original_answer": orig_a,
217
+ "status": status,
218
+ "modified_question": mod_q,
219
+ "modified_answer": mod_a,
220
+ "issue_type": issue_type,
221
+ "comment": comment,
222
+ "reviewer": reviewer
223
+ })
224
+
225
+ # 更新缓存
226
+ global _current_reviews
227
+ _current_reviews[qa_id] = {
228
+ "qa_id": qa_id, "status": status, "issue_type": issue_type,
229
+ "modified_question": mod_q, "modified_answer": mod_a, "comment": comment
230
+ }
231
+
232
+ stats = data_manager.get_review_stats()
233
+ return f"✅ 已保存! 总计: {stats['total']} | ✅正确: {stats['correct']} | ❌错误: {stats['incorrect']}"
234
+
235
+ def export_reviews_handler():
236
+ reviews = data_manager.get_all_reviews()
237
+ if not reviews:
238
+ return None
239
+ temp_path = os.path.join(tempfile.gettempdir(), "reviews_export.json")
240
+ with open(temp_path, 'w', encoding='utf-8') as f:
241
+ json.dump(reviews, f, ensure_ascii=False, indent=2)
242
+ return temp_path
243
+
244
+ # ============== 创建 Gradio 界面 ==============
245
 
246
  def create_ui():
247
+ custom_css = """
248
+ .chart-container { min-height: 780px; }
249
+ .control-panel { background: #f8f9fa; padding: 12px; border-radius: 8px; margin-bottom: 8px; }
250
+ """
251
+
252
+ with gr.Blocks(title="图表问答数据集审核系统", theme=gr.themes.Soft(), css=custom_css) as app:
253
+ gr.Markdown("# 📊 图表问答数据集审核系统\n\n审核图表问答对,使用 ← → 按钮切换图表")
254
 
255
+ # 顶部状态
256
+ with gr.Row():
257
+ status_text = gr.Textbox(label="统计", interactive=False, scale=2)
258
+ progress_text = gr.Textbox(label="进度", interactive=False, scale=1)
259
 
260
+ # 导航栏
261
  with gr.Row():
262
+ source_drop = gr.Dropdown(label="数据来源", choices=[], interactive=True, scale=1)
263
+ type_drop = gr.Dropdown(label="图表类型", choices=[], interactive=True, scale=1)
264
+ id_drop = gr.Dropdown(label="图表ID", choices=[], interactive=True, scale=1)
265
+ model_drop = gr.Dropdown(label="型", choices=[], interactive=True, scale=1)
266
+ reviewer = gr.Textbox(label="审核人", value="admin", interactive=True, scale=1)
267
+
268
+ with gr.Row():
269
+ prev_btn = gr.Button("⬅️ 上一个", size="sm")
270
+ next_btn = gr.Button("➡️ 下一个", size="sm")
271
+ export_btn = gr.Button("📦 导出记录", variant="secondary", size="sm")
272
+ download_file = gr.File(label="下载", visible=False)
273
+
274
+ # 主内容
275
+ with gr.Row():
276
+ # 左侧:图表
277
+ with gr.Column(scale=6):
278
+ html_display = gr.HTML(elem_classes=["chart-container"])
279
+ debug_info = gr.Textbox(label="调试", interactive=False, show_label=False)
280
+
281
+ # 右侧:审核
282
+ with gr.Column(scale=4):
283
+ with gr.Accordion("📝 图表元数据", open=False):
284
+ label_display = gr.Markdown()
285
 
286
+ gr.Markdown("### ❓ 问答审核")
287
+ qa_selector = gr.Radio(label="选择问答对", choices=[], interactive=True)
288
 
289
+ current_qa_id = gr.Textbox(visible=False, value="")
290
+ orig_q = gr.Textbox(label="原始问题", interactive=False, lines=2)
291
+ orig_a = gr.Textbox(label="原始答案", interactive=False)
 
 
292
 
293
+ gr.Markdown("---")
294
  status_radio = gr.Radio(
295
+ label="判定结果",
296
+ choices=[("正确", "correct"), ("错误", "incorrect"), ("✏️ 需修改", "needs_modification")],
297
+ value="correct",
298
+ interactive=True
299
+ )
300
+ issue_type = gr.Dropdown(
301
+ label="错误类型",
302
+ choices=["答案错误", "问题模糊", "图文不符", "其他"],
303
+ interactive=True
304
  )
305
+ mod_q = gr.Textbox(label="修改后问题", lines=2, interactive=True)
306
+ mod_a = gr.Textbox(label="修改后答案", interactive=True)
307
+ comment = gr.Textbox(label="备注", lines=2, interactive=True)
 
308
 
309
  save_btn = gr.Button("💾 保存审核", variant="primary")
310
+ save_msg = gr.Textbox(label="", visible=False)
 
 
 
 
 
 
 
311
 
312
+ # ===== 事件绑定 =====
313
+ app.load(fn=init_dataset, outputs=[source_drop])
 
 
 
314
 
315
+ source_drop.change(fn=update_chart_type_dropdown, inputs=[source_drop], outputs=[type_drop])
316
+ type_drop.change(fn=update_chart_dropdown, inputs=[source_drop, type_drop], outputs=[id_drop, model_drop])
317
 
318
+ load_outputs = [html_display, label_display, status_text, progress_text, qa_selector, debug_info]
319
+ id_drop.change(fn=load_chart_data, inputs=[source_drop, type_drop, id_drop, model_drop], outputs=load_outputs)
320
+ model_drop.change(fn=load_chart_data, inputs=[source_drop, type_drop, id_drop, model_drop], outputs=load_outputs)
321
+
322
+ qa_outputs = [current_qa_id, orig_q, orig_a, status_radio, issue_type, mod_q, mod_a, comment]
323
+ qa_selector.change(fn=on_qa_select, inputs=[qa_selector], outputs=qa_outputs)
324
+
325
+ nav_outputs = [source_drop, type_drop, id_drop, model_drop]
326
+ prev_btn.click(fn=navigate_prev, outputs=nav_outputs)
327
+ next_btn.click(fn=navigate_next, outputs=nav_outputs)
328
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
329
  save_btn.click(
330
+ fn=save_review_handler,
331
+ inputs=[current_qa_id, id_drop, source_drop, type_drop, model_drop, orig_q, orig_a, status_radio, mod_q, mod_a, issue_type, comment, reviewer],
332
+ outputs=[save_msg]
333
+ ).then(fn=lambda: gr.Textbox(visible=True), outputs=[save_msg])
334
 
335
+ export_btn.click(fn=export_reviews_handler, outputs=[download_file]).then(fn=lambda: gr.File(visible=True), outputs=[download_file])
336
+
337
+ return app
 
 
 
338
 
339
  if __name__ == "__main__":
340
+ app = create_ui()
341
+ app.launch(server_name="0.0.0.0", server_port=7860, show_api=False)