feat: 完成T-204旧字段回写
实现 Excel 旧标题与旧封面路径回写原文件,按 source_file_abs/source_sheet/source_row 定位行,支持输出列自动追加、xlsm 保留 VBA、文件占用中文错误提示与 export_copy 另存副本。 Tab① 增加回写旧数据按钮与 WriteBackWorker,后台调用 excel.write_back,锁文件时提示关闭后重试;补充 Excel 与 GUI 单测覆盖回写、锁文件、另存副本和 worker 调用。 同步 harness 文档:T-204 标记完成,新增 T-204b 记录采集完成自动回写缺口,扩展 T-205 覆盖 Chrome 未启动/未登录引导保护,并更新 current-state、routes、api、architecture、requirements 与 progress。
This commit is contained in:
+91
@@ -206,11 +206,14 @@ if QT_IMPORT_ERROR is None:
|
||||
self.last_import_stats = None
|
||||
self.collect_worker = None
|
||||
self.collect_thread = None
|
||||
self.write_back_worker = None
|
||||
self.write_back_thread = None
|
||||
|
||||
self.import_button = QPushButton("导入 Excel...")
|
||||
self.refresh_button = QPushButton("刷新")
|
||||
self.collect_button = QPushButton("采集旧标题/旧封面")
|
||||
self.stop_collect_button = QPushButton("停止")
|
||||
self.write_back_button = QPushButton("回写旧数据到 Excel")
|
||||
self.stop_collect_button.setEnabled(False)
|
||||
|
||||
toolbar = QHBoxLayout()
|
||||
@@ -218,6 +221,7 @@ if QT_IMPORT_ERROR is None:
|
||||
toolbar.addWidget(self.refresh_button)
|
||||
toolbar.addWidget(self.collect_button)
|
||||
toolbar.addWidget(self.stop_collect_button)
|
||||
toolbar.addWidget(self.write_back_button)
|
||||
toolbar.addStretch(1)
|
||||
|
||||
self.summary_label = QLabel("未导入任务")
|
||||
@@ -254,6 +258,7 @@ if QT_IMPORT_ERROR is None:
|
||||
self.refresh_button.clicked.connect(self.refresh_tasks)
|
||||
self.collect_button.clicked.connect(self.collect_old_data)
|
||||
self.stop_collect_button.clicked.connect(self.stop_collect)
|
||||
self.write_back_button.clicked.connect(self.write_back_old_data)
|
||||
self.show_all_button.clicked.connect(self.show_all_tasks)
|
||||
self.show_unmatched_button.clicked.connect(self.show_unmatched_tasks)
|
||||
|
||||
@@ -339,17 +344,57 @@ if QT_IMPORT_ERROR is None:
|
||||
self.collect_worker.cancel()
|
||||
self._set_status("正在停止采集...")
|
||||
|
||||
def write_back_old_data(self, checked=False):
|
||||
batch_id = self._active_batch_id()
|
||||
if not batch_id:
|
||||
self._set_status("没有可回写批次")
|
||||
return
|
||||
worker = WriteBackWorker(batch_id, db_path=self.db_path)
|
||||
worker.failed.connect(self._on_write_back_failed)
|
||||
worker.finished.connect(self._on_write_back_finished)
|
||||
thread = run_worker(worker, thread_name="WriteBackWorker", start=False)
|
||||
thread.finished.connect(lambda: self._forget_write_back_thread(thread))
|
||||
self.write_back_worker = worker
|
||||
self.write_back_thread = thread
|
||||
self._set_write_back_running(True)
|
||||
self._set_status("正在回写旧数据到 Excel...")
|
||||
thread.start()
|
||||
|
||||
def _active_batch_id(self):
|
||||
if self.current_batch_id:
|
||||
return self.current_batch_id
|
||||
batch_ids = {
|
||||
task.batch_id
|
||||
for task in self.model.all_tasks
|
||||
if getattr(task, "batch_id", None)
|
||||
}
|
||||
if len(batch_ids) == 1:
|
||||
return next(iter(batch_ids))
|
||||
return None
|
||||
|
||||
def _set_collect_running(self, running):
|
||||
self.import_button.setEnabled(not running)
|
||||
self.refresh_button.setEnabled(not running)
|
||||
self.collect_button.setEnabled(not running)
|
||||
self.write_back_button.setEnabled(not running)
|
||||
self.stop_collect_button.setEnabled(running)
|
||||
|
||||
def _set_write_back_running(self, running):
|
||||
self.import_button.setEnabled(not running)
|
||||
self.refresh_button.setEnabled(not running)
|
||||
self.collect_button.setEnabled(not running)
|
||||
self.write_back_button.setEnabled(not running)
|
||||
|
||||
def _forget_collect_thread(self, thread):
|
||||
if self.collect_thread is thread:
|
||||
self.collect_thread = None
|
||||
self.collect_worker = None
|
||||
|
||||
def _forget_write_back_thread(self, thread):
|
||||
if self.write_back_thread is thread:
|
||||
self.write_back_thread = None
|
||||
self.write_back_worker = None
|
||||
|
||||
def _on_collect_progress(self, payload):
|
||||
self._set_status(
|
||||
"采集进度:{done}/{total},成功{collected},略过{skipped},失败{failed}".format(
|
||||
@@ -388,6 +433,27 @@ if QT_IMPORT_ERROR is None:
|
||||
)
|
||||
)
|
||||
|
||||
def _on_write_back_failed(self, task_id, error):
|
||||
message = f"Excel 回写失败:{error}"
|
||||
if "被占用" in str(error):
|
||||
message += "\n请关闭原 Excel 后重试;SQLite 已保留采集结果,也可另存副本。"
|
||||
QMessageBox.warning(self, "回写旧数据", message)
|
||||
self._set_status(message.replace("\n", " "))
|
||||
|
||||
def _on_write_back_finished(self, payload):
|
||||
self._set_write_back_running(False)
|
||||
if payload.get("ok") is False:
|
||||
error = payload.get("error") or "未知错误"
|
||||
self._set_status(f"Excel 回写失败:{error}")
|
||||
return
|
||||
self.refresh_tasks()
|
||||
self._set_status(
|
||||
"Excel 回写完成:文件{files},行{rows}".format(
|
||||
files=payload.get("files", 0),
|
||||
rows=payload.get("rows", 0),
|
||||
)
|
||||
)
|
||||
|
||||
def show_all_tasks(self, checked=False):
|
||||
self.model.set_filter_mode("all")
|
||||
self._update_empty_label(len(self.model.all_tasks))
|
||||
@@ -669,6 +735,31 @@ if QT_IMPORT_ERROR is None:
|
||||
)
|
||||
|
||||
|
||||
class WriteBackWorker(BaseWorker):
|
||||
"""Write collected old fields back to Excel in a background thread."""
|
||||
|
||||
def __init__(self, batch_id, db_path=None, excel_path=None):
|
||||
super().__init__()
|
||||
self.batch_id = batch_id
|
||||
self.db_path = db_path
|
||||
self.excel_path = excel_path
|
||||
|
||||
def execute(self):
|
||||
result = excel.write_back(
|
||||
self.batch_id,
|
||||
excel_path=self.excel_path,
|
||||
path=self.db_path,
|
||||
)
|
||||
self.progress.emit(
|
||||
{
|
||||
"done": result.get("rows", 0),
|
||||
"total": result.get("rows", 0),
|
||||
"files": result.get("files", 0),
|
||||
}
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
class AccountLoginCheckWorker(BaseWorker):
|
||||
def __init__(self, account, db_path=None, config=None, timeout=8):
|
||||
super().__init__()
|
||||
|
||||
Reference in New Issue
Block a user