adddrett commited on
Commit
c5b4bef
·
1 Parent(s): 90d91c3
Files changed (1) hide show
  1. app.py +74 -132
app.py CHANGED
@@ -1,224 +1,166 @@
1
  import gradio as gr
2
  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 = []
10
- self.current_idx = -1
11
-
12
- def sync_paths(self):
13
- self.all_paths = data_manager.get_all_chart_paths()
14
-
15
- def get_nav_target(self, direction):
16
- new_idx = self.current_idx + direction
17
- if 0 <= new_idx < len(self.all_paths):
18
- self.current_idx = new_idx
19
- return self.all_paths[new_idx]
20
- return None
21
-
22
- nav_state = ReviewState()
23
-
24
  # ============== 工具函数 ==============
25
  def to_html_frame(html_content):
26
  if not html_content:
27
  return '<div style="padding:20px;text-align:center;">请选择数据进行加载</div>'
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):
42
  charts = data_manager.get_chart_list(source, c_type)
43
  struct = data_manager.get_dataset_structure()
44
  models = struct.get('sources', {}).get(source, {}).get('chart_types', {}).get(c_type, {}).get('models', [])
45
  return (
46
- gr.update(choices=charts, value=charts[0] if charts else None),
47
- gr.update(choices=models, value=models[0] if models else None)
48
  )
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,
72
- meta_md,
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:
196
- return [
197
- gr.update(value=target['source']),
198
- gr.update(value=target['chart_type']),
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
- )
 
1
  import gradio as gr
2
  import json
3
  import base64
4
+ import os
5
  from data_manager import data_manager
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  # ============== 工具函数 ==============
8
  def to_html_frame(html_content):
9
  if not html_content:
10
  return '<div style="padding:20px;text-align:center;">请选择数据进行加载</div>'
11
+ try:
12
+ b64_content = base64.b64encode(html_content.encode('utf-8')).decode('utf-8')
13
+ return f'<iframe src="data:text/html;base64,{b64_content}" style="width:100%;height:550px;border:2px solid #eee;border-radius:8px;"></iframe>'
14
+ except:
15
+ return '<div style="color:red;">HTML 渲染失败</div>'
16
 
17
  def get_stats_display():
18
+ s = data_manager.get_review_stats()
19
+ return f"✅ 正确: {s['correct']} | ❌ 错误: {s['incorrect']} | 🔧 优化: {s.get('modified', 0)} | 总计: {s['total']}"
20
 
21
+ # ============== 核心逻辑 ==============
22
  def handle_source_change(source):
23
  struct = data_manager.get_dataset_structure()
24
  types = list(struct.get('sources', {}).get(source, {}).get('chart_types', {}).keys())
25
+ return gr.update(choices=types, value=types[0] if types else "")
26
 
27
  def handle_type_change(source, c_type):
28
  charts = data_manager.get_chart_list(source, c_type)
29
  struct = data_manager.get_dataset_structure()
30
  models = struct.get('sources', {}).get(source, {}).get('chart_types', {}).get(c_type, {}).get('models', [])
31
  return (
32
+ gr.update(choices=charts, value=charts[0] if charts else ""),
33
+ gr.update(choices=models, value=models[0] if models else "")
34
  )
35
 
36
  def handle_load(source, c_type, c_id, model):
37
  if not all([source, c_type, c_id, model]):
38
+ return [gr.update()] * 7 # 这里的数量必须严格对应 outputs
39
 
40
  chart_data = data_manager.get_chart_data(source, c_type, c_id)
41
  qa_list = data_manager.get_qa_list(source, c_type, model, c_id)
42
 
43
+ # 构造 Radio 选项,添加 ID 标记以便解析
44
+ radio_choices = [f"[{i}] {q.question[:30]}..." for i, q in enumerate(qa_list)]
 
 
 
 
 
 
 
 
 
 
45
 
46
  return [
47
+ to_html_frame(chart_data.get('html_content', '')),
48
+ "\n".join([f"- **{k}**: {v}" for k, v in chart_data.get('label_info', {}).items()]) if chart_data.get('label_info') else "无元数据",
49
+ json.dumps([{"id": q.id, "q": q.question, "a": q.answer} for q in qa_list], ensure_ascii=False),
50
+ get_stats_display(),
 
51
  f"{source} / {c_type} / {c_id}",
52
  gr.update(choices=radio_choices, value=radio_choices[0] if radio_choices else None),
53
+ "" # 清空备注栏
54
  ]
55
 
56
  def handle_qa_select(selection, qa_json):
57
  if not selection or not qa_json:
58
+ return ["", "", "", ""]
59
  try:
60
  qas = json.loads(qa_json)
61
+ # 解析索引,形如 "[0] 标题..."
62
+ idx = int(selection.split(']')[0].replace('[', ''))
63
  curr = qas[idx]
64
  return [curr['id'], curr['q'], curr['a'], ""]
65
+ except Exception as e:
66
+ print(f"解析QA失败: {e}")
67
+ return ["", "", "", ""]
68
 
69
  # ============== UI 布局 ==============
70
  def create_ui():
71
+ with gr.Blocks(title="ChartQA 审核系统", theme=gr.themes.Default()) as demo:
72
+ # 内存状态
73
  qa_store = gr.State(value="[]")
74
 
75
+ gr.Markdown("# 📑 图表问答数据集审核终端")
76
+
 
 
 
 
77
  with gr.Row():
78
  with gr.Column(scale=4):
79
  with gr.Row():
80
+ src_dd = gr.Dropdown(label="1. 数据源", choices=[])
81
  typ_dd = gr.Dropdown(label="2. 图表类型")
82
  id_dd = gr.Dropdown(label="3. 图表 ID")
83
  mdl_dd = gr.Dropdown(label="4. 评估模型")
84
 
85
+ chart_view = gr.HTML()
86
+ path_info = gr.Text(label="当前路径", interactive=False)
87
 
88
  with gr.Column(scale=2):
89
  with gr.Group():
90
+ stats_txt = gr.Text(label="审核统计", value=get_stats_display(), interactive=False)
91
+ export_btn = gr.Button("📤 导出并下载 JSON", variant="secondary")
92
+ download_file = gr.File(label="下载链接", visible=False)
93
+
94
+ with gr.Accordion("元数据 (Meta)", open=False):
95
+ meta_md = gr.Markdown()
96
 
97
  gr.Markdown("---")
98
+ qa_radio = gr.Radio(label="题目列表", choices=[])
99
 
100
  with gr.Group():
101
+ curr_qid = gr.Text(label="当前 QA ID", interactive=False)
102
+ q_disp = gr.Text(label="问题", lines=2)
103
+ a_disp = gr.Text(label="答案", lines=2)
 
104
  status_opt = gr.Radio(
105
+ label="结论",
106
+ choices=[("正确", "correct"), ("错误", "incorrect"), ("优化", "modified")],
107
  value="correct"
108
  )
109
+ comment = gr.Text(label="备注")
110
+ save_btn = gr.Button("💾 提交审核", variant="primary")
 
 
 
 
 
 
111
 
112
  # --- 事件绑定 ---
113
+
114
+ # 1. 初始
115
  demo.load(
116
  fn=lambda: gr.update(choices=list(data_manager.get_dataset_structure().get('sources', {}).keys())),
117
  outputs=[src_dd]
118
  )
119
 
120
+ # 2. 联动
121
  src_dd.change(handle_source_change, inputs=[src_dd], outputs=[typ_dd])
122
  typ_dd.change(handle_type_change, inputs=[src_dd, typ_dd], outputs=[id_dd, mdl_dd])
123
 
124
+ # 3. 加载 (确保 outputs 数量为 7)
125
+ load_outputs = [chart_view, meta_md, qa_store, stats_txt, path_info, qa_radio, comment]
126
+ id_dd.change(handle_load, inputs=[src_dd, typ_dd, id_dd, mdl_dd], outputs=load_outputs)
127
+ mdl_dd.change(handle_load, inputs=[src_dd, typ_dd, id_dd, mdl_dd], outputs=load_outputs)
128
 
129
+ # 4. 题目选择
130
  qa_radio.change(
131
  handle_qa_select,
132
  inputs=[qa_radio, qa_store],
133
+ outputs=[curr_qid, q_disp, a_disp, comment]
134
  )
135
 
136
+ # 5. 保存 (增加反馈)
137
+ def on_save(qid, cid, src, status, cmt, model, c_type):
138
  if not qid:
139
+ return gr.update(value="请先选择题目"), get_stats_display()
 
140
 
141
  data_manager.save_review({
142
+ "qa_id": qid, "chart_id": cid, "source": src,
143
+ "chart_type": c_type, "model": model,
144
+ "status": status, "comment": cmt
 
 
 
 
 
145
  })
146
+ # 弹出提示
147
+ gr.Info(f"已保存记录: {qid}")
148
+ return gr.update(value=cmt), get_stats_display()
149
 
150
  save_btn.click(
151
+ on_save,
152
+ inputs=[curr_qid, id_dd, src_dd, status_opt, comment, mdl_dd, typ_dd],
153
+ outputs=[comment, stats_txt]
154
  )
155
 
156
+ # 6. 导
157
+ export_btn.click(
158
+ fn=lambda: gr.update(value=data_manager.export_reviews(), visible=True),
159
+ outputs=[download_file]
160
+ )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
161
 
162
  return demo
163
 
164
  if __name__ == "__main__":
 
165
  app = create_ui()
166
+ app.launch(server_name="0.0.0.0", server_port=7860, show_api=False)