adddrett commited on
Commit
90d91c3
·
1 Parent(s): afaa666
Files changed (1) hide show
  1. app.py +78 -62
app.py CHANGED
@@ -3,7 +3,7 @@ import json
3
  import base64
4
  from data_manager import data_manager
5
 
6
- # ============== 状态管理 (封装逻辑) ==============
7
  class ReviewState:
8
  def __init__(self):
9
  self.all_paths = []
@@ -28,11 +28,14 @@ def to_html_frame(html_content):
28
  b64_content = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
29
  return f'<iframe src="data:text/html;base64,{b64_content}" style="width:100%;height:600px;border:none;"></iframe>'
30
 
 
 
 
 
31
  # ============== 交互逻辑 ==============
32
  def handle_source_change(source):
33
  struct = data_manager.get_dataset_structure()
34
  types = list(struct.get('sources', {}).get(source, {}).get('chart_types', {}).keys())
35
- # 默认选中第一个
36
  return gr.update(choices=types, value=types[0] if types else None)
37
 
38
  def handle_type_change(source, c_type):
@@ -46,28 +49,23 @@ def handle_type_change(source, c_type):
46
 
47
  def handle_load(source, c_type, c_id, model):
48
  if not all([source, c_type, c_id, model]):
49
- # 必须返回 8 个 update 对象以匹配 outputs
50
  return [gr.update()] * 8
51
 
52
- # 获取数据
53
  chart_data = data_manager.get_chart_data(source, c_type, c_id)
54
  qa_list = data_manager.get_qa_list(source, c_type, model, c_id)
55
- stats = data_manager.get_review_stats()
56
 
57
- # 同步索引位置
58
  nav_state.sync_paths()
59
  for i, p in enumerate(nav_state.all_paths):
60
  if p['chart_id'] == c_id and p['model'] == model:
61
  nav_state.current_idx = i
62
  break
63
 
64
- # 准备 UI 数据
65
  html_code = to_html_frame(chart_data.get('html_content', ''))
66
  meta_md = "\n".join([f"- **{k}**: {v}" for k, v in chart_data.get('label_info', {}).items()])
67
  qa_json = json.dumps([{"id": q.id, "q": q.question, "a": q.answer} for q in qa_list])
68
- stats_str = f"✅{stats['correct']} | ❌{stats['incorrect']} | 总{stats['total']}"
69
  prog_str = f"{nav_state.current_idx + 1} / {len(nav_state.all_paths)}"
70
- radio_choices = [f"Q{i+1}: {q.question[:20]}..." for i, q in enumerate(qa_list)]
71
 
72
  return [
73
  html_code,
@@ -75,97 +73,123 @@ def handle_load(source, c_type, c_id, model):
75
  qa_json,
76
  stats_str,
77
  prog_str,
78
- f"{source}/{c_type}/{c_id}",
79
  gr.update(choices=radio_choices, value=radio_choices[0] if radio_choices else None),
80
- json.dumps({}) # 占位 review_store
81
  ]
82
 
83
  def handle_qa_select(selection, qa_json):
84
  if not selection or not qa_json:
85
- return [""] * 8
86
  try:
87
  qas = json.loads(qa_json)
88
  idx = int(selection.split(":")[0][1:]) - 1
89
  curr = qas[idx]
90
- return [curr['id'], curr['q'], curr['a'], "correct", "无", "", "", ""]
91
  except:
92
- return [""] * 8
93
 
94
  # ============== UI 布局 ==============
95
  def create_ui():
96
- with gr.Blocks(title="审核系统 V2", theme=gr.themes.Soft()) as demo:
97
- # 显式初始化 State,避免布尔值陷阱
98
  qa_store = gr.State(value="[]")
99
- review_store = gr.State(value="{}")
100
-
101
- gr.Markdown("## 📑 图表问答数据集审核终端")
102
 
 
 
 
 
 
 
103
  with gr.Row():
104
  with gr.Column(scale=4):
105
  with gr.Row():
106
- src_dd = gr.Dropdown(label="数据源", choices=["None"])
107
- typ_dd = gr.Dropdown(label="图表类型")
108
- id_dd = gr.Dropdown(label="图表 ID")
109
- mdl_dd = gr.Dropdown(label="评估模型")
110
 
111
- chart_view = gr.HTML(value='<div style="height:500px; background:#f0f0f0;"></div>')
112
- path_info = gr.Text(label="当前路径", interactive=False)
113
 
114
  with gr.Column(scale=2):
115
  with gr.Group():
116
- stats_txt = gr.Text(label="统计信息", interactive=False)
117
- prog_txt = gr.Text(label="审核进度", interactive=False)
118
 
119
- with gr.Accordion("元数据解析", open=False):
120
- meta_md = gr.Markdown()
121
 
122
  gr.Markdown("---")
123
- qa_radio = gr.Radio(label="题目列表", choices=[])
124
 
125
  with gr.Group():
126
- curr_qid = gr.Text(visible=False)
127
- q_disp = gr.Text(label="问题内容", lines=2)
128
- a_disp = gr.Text(label="标准答案")
129
 
130
  status_opt = gr.Radio(
131
  label="审核结论",
132
- choices=[("正确", "correct"), ("错误", "incorrect"), ("优化", "modified")],
133
  value="correct"
134
  )
135
- err_type = gr.Dropdown(label="错误分类", choices=["无", "事实错误", "逻辑错误", "图表无法读取"])
136
 
137
- comment = gr.Text(label="审核备注")
138
  save_btn = gr.Button("💾 提交单条审核", variant="primary")
139
 
140
  with gr.Row():
141
- prev_btn = gr.Button("⬅️ 上一图表")
142
- next_btn = gr.Button("➡️ 下一图表")
143
 
144
  # --- 事件绑定 ---
145
-
146
- # 初始化第一级
147
  demo.load(
148
  fn=lambda: gr.update(choices=list(data_manager.get_dataset_structure().get('sources', {}).keys())),
149
  outputs=[src_dd]
150
  )
151
 
152
- # 联动逻辑
153
  src_dd.change(handle_source_change, inputs=[src_dd], outputs=[typ_dd])
154
  typ_dd.change(handle_type_change, inputs=[src_dd, typ_dd], outputs=[id_dd, mdl_dd])
155
 
156
- # 加载数据 (严格匹配 8 output)
157
- load_event_outputs = [chart_view, meta_md, qa_store, stats_txt, prog_txt, path_info, qa_radio, review_store]
158
  id_dd.change(handle_load, inputs=[src_dd, typ_dd, id_dd, mdl_dd], outputs=load_event_outputs)
159
  mdl_dd.change(handle_load, inputs=[src_dd, typ_dd, id_dd, mdl_dd], outputs=load_event_outputs)
160
 
161
- # 题目切换
162
  qa_radio.change(
163
  handle_qa_select,
164
  inputs=[qa_radio, qa_store],
165
- outputs=[curr_qid, q_disp, a_disp, status_opt, err_type, gr.State(), gr.State(), comment]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  )
167
 
168
- # 导航逻辑
169
  def navigate(direction):
170
  target = nav_state.get_nav_target(direction)
171
  if target:
@@ -175,34 +199,26 @@ def create_ui():
175
  gr.update(value=target['chart_id']),
176
  gr.update(value=target['model'])
177
  ]
 
178
  return [gr.update()] * 4
179
 
180
  prev_btn.click(lambda: navigate(-1), outputs=[src_dd, typ_dd, id_dd, mdl_dd])
181
  next_btn.click(lambda: navigate(1), outputs=[src_dd, typ_dd, id_dd, mdl_dd])
182
 
183
- # 保存逻辑
184
- def quick_save(qid, cid, src, status, cmt):
185
- if not qid: return "无效操作"
186
- data_manager.save_review({
187
- "qa_id": qid, "chart_id": cid, "source": src,
188
- "status": status, "comment": cmt
189
- })
190
- return "已保存"
191
 
192
- save_btn.click(
193
- quick_save,
194
- inputs=[curr_qid, id_dd, src_dd, status_opt, comment],
195
- outputs=[gr.Text(visible=False)]
196
- )
197
 
198
  return demo
199
 
200
  if __name__ == "__main__":
 
201
  app = create_ui()
202
- # 强制禁用所有 API 预览功能
203
  app.launch(
204
  server_name="0.0.0.0",
205
  server_port=7860,
206
- show_api=False,
207
- max_threads=10
208
  )
 
3
  import base64
4
  from data_manager import data_manager
5
 
6
+ # ============== 状态管理 ==============
7
  class ReviewState:
8
  def __init__(self):
9
  self.all_paths = []
 
28
  b64_content = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
29
  return f'<iframe src="data:text/html;base64,{b64_content}" style="width:100%;height:600px;border:none;"></iframe>'
30
 
31
+ def get_stats_display():
32
+ stats = data_manager.get_review_stats()
33
+ return f"✅正确: {stats['correct']} | ❌错误: {stats['incorrect']} | 📝优化: {stats.get('modified', 0)} | 总计: {stats['total']}"
34
+
35
  # ============== 交互逻辑 ==============
36
  def handle_source_change(source):
37
  struct = data_manager.get_dataset_structure()
38
  types = list(struct.get('sources', {}).get(source, {}).get('chart_types', {}).keys())
 
39
  return gr.update(choices=types, value=types[0] if types else None)
40
 
41
  def handle_type_change(source, c_type):
 
49
 
50
  def handle_load(source, c_type, c_id, model):
51
  if not all([source, c_type, c_id, model]):
 
52
  return [gr.update()] * 8
53
 
 
54
  chart_data = data_manager.get_chart_data(source, c_type, c_id)
55
  qa_list = data_manager.get_qa_list(source, c_type, model, c_id)
 
56
 
 
57
  nav_state.sync_paths()
58
  for i, p in enumerate(nav_state.all_paths):
59
  if p['chart_id'] == c_id and p['model'] == model:
60
  nav_state.current_idx = i
61
  break
62
 
 
63
  html_code = to_html_frame(chart_data.get('html_content', ''))
64
  meta_md = "\n".join([f"- **{k}**: {v}" for k, v in chart_data.get('label_info', {}).items()])
65
  qa_json = json.dumps([{"id": q.id, "q": q.question, "a": q.answer} for q in qa_list])
66
+ stats_str = get_stats_display()
67
  prog_str = f"{nav_state.current_idx + 1} / {len(nav_state.all_paths)}"
68
+ radio_choices = [f"Q{i+1}: {q.question[:30]}..." for i, q in enumerate(qa_list)]
69
 
70
  return [
71
  html_code,
 
73
  qa_json,
74
  stats_str,
75
  prog_str,
76
+ f"{source} / {c_type} / {c_id}",
77
  gr.update(choices=radio_choices, value=radio_choices[0] if radio_choices else None),
78
+ "{}"
79
  ]
80
 
81
  def handle_qa_select(selection, qa_json):
82
  if not selection or not qa_json:
83
+ return [""] * 4
84
  try:
85
  qas = json.loads(qa_json)
86
  idx = int(selection.split(":")[0][1:]) - 1
87
  curr = qas[idx]
88
+ return [curr['id'], curr['q'], curr['a'], ""]
89
  except:
90
+ return [""] * 4
91
 
92
  # ============== UI 布局 ==============
93
  def create_ui():
94
+ with gr.Blocks(title="图表审核系统 V2", theme=gr.themes.Soft()) as demo:
 
95
  qa_store = gr.State(value="[]")
 
 
 
96
 
97
+ with gr.Row():
98
+ gr.Markdown("# 📊 图表问答数据集审核终端")
99
+ with gr.Column(min_width=200):
100
+ export_btn = gr.Button("📤 导出审核记录", variant="secondary")
101
+ download_file = gr.File(label="点击下载导出的文件", visible=False)
102
+
103
  with gr.Row():
104
  with gr.Column(scale=4):
105
  with gr.Row():
106
+ src_dd = gr.Dropdown(label="1. 数据源")
107
+ typ_dd = gr.Dropdown(label="2. 图表类型")
108
+ id_dd = gr.Dropdown(label="3. 图表 ID")
109
+ mdl_dd = gr.Dropdown(label="4. 评估模型")
110
 
111
+ chart_view = gr.HTML(value='<div style="height:550px; background:#f9f9f9; border-radius:8px;"></div>')
112
+ path_info = gr.Text(label="当前数据路径", interactive=False)
113
 
114
  with gr.Column(scale=2):
115
  with gr.Group():
116
+ stats_txt = gr.Text(label="全库审核统计", value=get_stats_display(), interactive=False)
117
+ prog_txt = gr.Text(label="当前库进度", interactive=False)
118
 
119
+ with gr.Accordion("图表元数据 (Meta)", open=False):
120
+ meta_md = gr.Markdown("等待加载...")
121
 
122
  gr.Markdown("---")
123
+ qa_radio = gr.Radio(label="题目快速切换", choices=[])
124
 
125
  with gr.Group():
126
+ curr_qid = gr.Text(label="QA ID", interactive=False, visible=True)
127
+ q_disp = gr.Text(label="问题 (Question)", lines=3)
128
+ a_disp = gr.Text(label="答案 (Answer)", lines=3)
129
 
130
  status_opt = gr.Radio(
131
  label="审核结论",
132
+ choices=[("正确", "correct"), ("错误", "incorrect"), ("🔧 需优化", "modified")],
133
  value="correct"
134
  )
135
+ err_type = gr.Dropdown(label="错误分类 (可选)", choices=["无", "事实错误", "逻辑错误", "图表无法读取", "其他"])
136
 
137
+ comment = gr.Text(label="审核备注 (选填)")
138
  save_btn = gr.Button("💾 提交单条审核", variant="primary")
139
 
140
  with gr.Row():
141
+ prev_btn = gr.Button("⬅️ 上一图表")
142
+ next_btn = gr.Button("➡️ 下一图表")
143
 
144
  # --- 事件绑定 ---
145
+
146
+ # 1. 初始加载
147
  demo.load(
148
  fn=lambda: gr.update(choices=list(data_manager.get_dataset_structure().get('sources', {}).keys())),
149
  outputs=[src_dd]
150
  )
151
 
152
+ # 2. 联动逻辑
153
  src_dd.change(handle_source_change, inputs=[src_dd], outputs=[typ_dd])
154
  typ_dd.change(handle_type_change, inputs=[src_dd, typ_dd], outputs=[id_dd, mdl_dd])
155
 
156
+ load_event_outputs = [chart_view, meta_md, qa_store, stats_txt, prog_txt, path_info, qa_radio, comment]
 
157
  id_dd.change(handle_load, inputs=[src_dd, typ_dd, id_dd, mdl_dd], outputs=load_event_outputs)
158
  mdl_dd.change(handle_load, inputs=[src_dd, typ_dd, id_dd, mdl_dd], outputs=load_event_outputs)
159
 
160
+ # 3. 题目切换
161
  qa_radio.change(
162
  handle_qa_select,
163
  inputs=[qa_radio, qa_store],
164
+ outputs=[curr_qid, q_disp, a_disp, comment]
165
+ )
166
+
167
+ # 4. 保存功能 (带反馈和刷新)
168
+ def process_save(qid, cid, src, status, e_type, cmt, model, c_type):
169
+ if not qid:
170
+ gr.Warning("请先选择一条题目!")
171
+ return get_stats_display()
172
+
173
+ data_manager.save_review({
174
+ "qa_id": qid,
175
+ "chart_id": cid,
176
+ "source": src,
177
+ "chart_type": c_type,
178
+ "model": model,
179
+ "status": status,
180
+ "issue_type": e_type,
181
+ "comment": cmt
182
+ })
183
+ gr.Info(f"保存成功: {qid}")
184
+ return get_stats_display()
185
+
186
+ save_btn.click(
187
+ process_save,
188
+ inputs=[curr_qid, id_dd, src_dd, status_opt, err_type, comment, mdl_dd, typ_dd],
189
+ outputs=[stats_txt] # 保存后刷新统计数字
190
  )
191
 
192
+ # 5. 导航逻辑
193
  def navigate(direction):
194
  target = nav_state.get_nav_target(direction)
195
  if target:
 
199
  gr.update(value=target['chart_id']),
200
  gr.update(value=target['model'])
201
  ]
202
+ gr.Warning("已到达边界")
203
  return [gr.update()] * 4
204
 
205
  prev_btn.click(lambda: navigate(-1), outputs=[src_dd, typ_dd, id_dd, mdl_dd])
206
  next_btn.click(lambda: navigate(1), outputs=[src_dd, typ_dd, id_dd, mdl_dd])
207
 
208
+ # 6. 导出功能
209
+ def handle_export():
210
+ file_path = data_manager.export_reviews()
211
+ return gr.update(value=file_path, visible=True)
 
 
 
 
212
 
213
+ export_btn.click(handle_export, outputs=[download_file])
 
 
 
 
214
 
215
  return demo
216
 
217
  if __name__ == "__main__":
218
+ # 关闭所有可能干扰的 API 解析
219
  app = create_ui()
 
220
  app.launch(
221
  server_name="0.0.0.0",
222
  server_port=7860,
223
+ show_api=False
 
224
  )