Spaces:
Runtime error
Runtime error
File size: 8,732 Bytes
34ab05f 7cf0abc 34ab05f 7cf0abc 34ab05f 7cf0abc 34ab05f 099f191 34ab05f 7cf0abc 34ab05f 5d89d32 5005d6f 5d89d32 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | import os, tempfile, pathlib
import gradio as gr
import pandas as pd
import zipfile, time
from core_numeric import (
SUFFIX, CATEGORY_TITLES,
classify_row, _is_blank,
is_category_header_row, is_allowed_category_name,
find_header_row_by_probe
)
# ====== .xls 专用依赖(只在 .xls 分支用) ======
import xlrd # 读 .xls
from xlutils.copy import copy as xls_copy # 将 xlrd.Book -> xlwt.Workbook
import xlwt # 写回 .xls(BIFF8)
# ====== .xlsx 读写(现代路径) ======
import openpyxl
def _safe_basename(path: str) -> str:
return os.path.splitext(os.path.basename(path))[0]
def process_xls(in_path: str) -> str:
"""
在原工作表末尾新增 4 列:
IsTrueNumericMismatch / SrcNumbers / TgtNumbers / Note
并写回为 *_numeric_tagged.xls
"""
# 读取并保留样式信息
# 注意:formatting_info=True 仅对 .xls 生效,不能用于 .xlsx。:contentReference[oaicite:5]{index=5}
book = xlrd.open_workbook(in_path, formatting_info=True)
wbook = xls_copy(book)
sh_in = book.sheet_by_index(0)
sh_out = wbook.get_sheet(0)
nrows, ncols = sh_in.nrows, sh_in.ncols
# 找“Source/Target”表头行
def _cell(r, c):
try:
return sh_in.cell_value(r, c)
except Exception:
return ""
hdr = find_header_row_by_probe(_cell, max_scan=min(80, nrows))
# 追加 4 列写结果
base_col = ncols
headers = ["IsTrueNumericMismatch", "SrcNumbers", "TgtNumbers", "Note"]
if hdr >= 0:
for i, h in enumerate(headers):
sh_out.write(hdr, base_col + i, h)
# 遍历行并写入结果(保留原结构;不改变/删除任何行)
in_allowed = False
for r in range(hdr + 1 if hdr >= 0 else 0, nrows):
a = _cell(r, 0) # A: Category or "FileName (SegmentID)"
c = _cell(r, 2) # C: Source
d = _cell(r, 3) # D: Target
# 识别“类别标题行”
if is_category_header_row(a, c, d):
in_allowed = is_allowed_category_name(a)
continue
# 非允许类别(不是 Numeric mismatch)→ 不写结果
if not in_allowed:
continue
# 忽略纯空白行
if _is_blank(c) and _is_blank(d):
continue
# 计算并写入
tflag, src_s, tgt_s, note = classify_row(str(c or ""), str(d or ""))
sh_out.write(r, base_col + 0, "TRUE" if tflag else "FALSE")
sh_out.write(r, base_col + 1, src_s)
sh_out.write(r, base_col + 2, tgt_s)
sh_out.write(r, base_col + 3, note)
# 保存为 *_numeric_tagged.xls
out_path = os.path.join(
tempfile.gettempdir(),
f"{_safe_basename(in_path)}{SUFFIX}.xls"
)
wbook.save(out_path)
return out_path
def _read_table_generic(path: str) -> pd.DataFrame:
ext = pathlib.Path(path).suffix.lower()
if ext == ".csv":
return pd.read_csv(path, dtype=str, encoding="utf-8", keep_default_na=False)
elif ext == ".xls":
# pandas 读 .xls 也会用 xlrd 引擎;此路径仅用于 .xlsx 逻辑,.xls 我们已经直接“写回”。:contentReference[oaicite:6]{index=6}
return pd.read_excel(path, dtype=str, engine="xlrd")
else:
return pd.read_excel(path, dtype=str, engine="openpyxl")
def _reform_and_tag_df(df_raw: pd.DataFrame) -> pd.DataFrame:
"""面向 .xlsx/.csv:保留 'Numeric mismatch' 相关行,生成结构化输出表"""
# 探测表头行
def _probe(r, c):
try:
v = df_raw.iat[r, c]
return str(v) if pd.notna(v) else ""
except Exception:
return ""
hdr = find_header_row_by_probe(_probe, max_scan=min(80, len(df_raw)))
df = df_raw.iloc[hdr+1:].copy() if hdr >= 0 else df_raw.copy()
while df.shape[1] < 5:
df[df.shape[1]] = ""
colA, colB, colC, colD, colE = df.columns[0], df.columns[1], df.columns[2], df.columns[3], df.columns[4]
# 仅处理 Numeric mismatch 类别
keep, in_allowed = [], False
for _, row in df.iterrows():
a, c, d = row.get(colA, ""), row.get(colC, None), row.get(colD, None)
if is_category_header_row(a, c, d):
in_allowed = is_allowed_category_name(a); keep.append(False)
else:
keep.append(in_allowed)
df = df[pd.Series(keep, index=df.index)]
def is_data_row(row):
a_norm = str(row.get(colA, "")).strip().lower()
if a_norm in CATEGORY_TITLES: return False
return not (_is_blank(row.get(colC, None)) and _is_blank(row.get(colD, None)))
df = df[df.apply(is_data_row, axis=1)].copy()
# 计算 4 列结果
out_cols = ["IsTrueNumericMismatch", "SrcNumbers", "TgtNumbers", "Note"]
df_res = pd.DataFrame(columns=out_cols, index=df.index)
for i, row in df.iterrows():
tflag, src_s, tgt_s, note = classify_row(str(row.get(colC, "") or ""), str(row.get(colD, "") or ""))
df_res.at[i, "IsTrueNumericMismatch"] = bool(tflag)
df_res.at[i, "SrcNumbers"] = src_s
df_res.at[i, "TgtNumbers"] = tgt_s
df_res.at[i, "Note"] = note
# 拼出导出表(保留关注列 + 4 列结论)
out = pd.DataFrame({
"A": df[colA],
"Percent": df[colB],
"Source": df[colC],
"Target": df[colD],
"Comments": df[colE],
}).join(df_res)
return out
def process_xlsx_or_csv(in_path: str) -> str:
df_raw = _read_table_generic(in_path)
out_df = _reform_and_tag_df(df_raw)
out_path = os.path.join(
tempfile.gettempdir(),
f"{_safe_basename(in_path)}{SUFFIX}.xlsx"
)
out_df.to_excel(out_path, index=False)
# 设置筛选/冻结等属性(打开时生效)
wb = openpyxl.load_workbook(out_path); ws = wb.active
from openpyxl.utils import get_column_letter
ws.auto_filter.ref = f"A1:{get_column_letter(ws.max_column)}{ws.max_row}"
ws.freeze_panes = "A2"
wb.save(out_path)
return out_path
def process_files(files, pack_zip):
outputs = []
for f in files:
path = f.name
ext = pathlib.Path(path).suffix.lower()
if ext == ".xls":
outputs.append(process_xls(path))
elif ext in [".xlsx", ".csv"]:
outputs.append(process_xlsx_or_csv(path))
else:
raise gr.Error(f"不支持的文件类型:{ext}")
zip_path = None
if pack_zip and outputs:
# 以时间戳避免同名覆盖
stamp = time.strftime("%Y%m%d-%H%M%S")
zip_path = os.path.join(
tempfile.gettempdir(), f"XbenchAssistant_{stamp}.zip"
)
with zipfile.ZipFile(zip_path, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for p in outputs:
zf.write(p, arcname=os.path.basename(p))
return outputs, (zip_path or "")
with gr.Blocks(title="XbenchAssistant") as demo:
gr.Image(value="logo.png", show_label=False, height=80)
gr.Markdown("### XbenchAssistant|Xbench QA 报告 · 数字一致性去噪与打标\n"
"上传 `.xls/.xlsx/.csv`;\n"
"- `.xls`:在原工作表**追加 4 列**后,导出为 `*_numeric_tagged.xls`;\n"
"- `.xlsx/.csv`:产出结构化 `*_numeric_tagged.xlsx`。")
in_files = gr.File(label="上传 Xbench 报告(可多选)", file_count="multiple",
file_types=[".xls", ".xlsx", ".csv"])
with gr.Row():
pack_zip = gr.Checkbox(value=True, label="打包为 ZIP(推荐)")
btn = gr.Button("开始处理", variant="primary")
out_files = gr.File(label="逐个下载(可多选点击)", interactive=False)
zip_dl = gr.DownloadButton(label="⬇️ 一键下载 ZIP(处理后打包)", visible=True)
# 现在返回两个输出:1) 文件列表 2) zip 路径
btn.click(process_files, inputs=[in_files, pack_zip], outputs=[out_files, zip_dl])
if __name__ == "__main__":
import os
# 从 HF 的 Secrets 里读取(Settings → Secrets,新建 APP_USER_1/APP_PASS_1/APP_USER_2/APP_PASS_2)
USER1 = os.getenv("APP_USER_1", "")
PASS1 = os.getenv("APP_PASS_1", "")
USER2 = os.getenv("APP_USER_2", "")
PASS2 = os.getenv("APP_PASS_2", "")
AUTH_PAIRS = []
if USER1 and PASS1:
AUTH_PAIRS.append((USER1, PASS1))
if USER2 and PASS2:
AUTH_PAIRS.append((USER2, PASS2))
# 登录页提示可自定义
AUTH_MSG = "ECI 内部工具|请输入用户名与密码"
# 多账户认证:Gradio 的 launch(auth=...) 支持元组或列表形式
demo.launch(
auth=AUTH_PAIRS if AUTH_PAIRS else None,
auth_message=AUTH_MSG,
ssr_mode=False,
show_error=True
)
|