feat(ai-studio): streamline project workspace
This commit is contained in:
+104
-58
@@ -238,7 +238,7 @@ class ImageStudioPreviewDialog(QDialog):
|
||||
class ImageStudioTab(QWidget):
|
||||
"""Sixth tab: project-based AI image studio."""
|
||||
|
||||
PROJECT_COLUMNS = ["项目", "账号", "商品ID", "更新时间"]
|
||||
PROJECT_COLUMNS = ["店铺", "商品ID", "更新时间"]
|
||||
ORIGINAL_COLUMNS = ["序号", "状态", "远程地址"]
|
||||
POOL_COLUMNS = ["类型", "比例", "状态", "来源", "本地文件"]
|
||||
JOB_STATUS_LABELS = {
|
||||
@@ -277,6 +277,7 @@ class ImageStudioTab(QWidget):
|
||||
self.selected_source_asset_id = None
|
||||
self._running_worker = None
|
||||
self._running_thread = None
|
||||
self._operation_running = False
|
||||
self._worker_error_handled = False
|
||||
self._download_open_after = {}
|
||||
|
||||
@@ -321,31 +322,30 @@ class ImageStudioTab(QWidget):
|
||||
self.item_id_edit.setObjectName("imageStudioItemIdEdit")
|
||||
self.item_id_edit.setPlaceholderText("商品ID")
|
||||
self.item_id_edit.setMinimumWidth(160)
|
||||
self.open_project_button = QPushButton("打开商品项目")
|
||||
self.open_project_button.setObjectName("imageStudioOpenProjectButton")
|
||||
|
||||
self.current_project_label = QLabel("未打开商品项目")
|
||||
self.current_project_label = QLabel("未选择商品")
|
||||
self.current_project_label.setObjectName("imageStudioCurrentProjectLabel")
|
||||
self.current_project_label.setMinimumWidth(220)
|
||||
self.autosave_label = QLabel("选择账号并输入商品ID后打开")
|
||||
self.autosave_label = QLabel("选择店铺并输入商品ID后拉取")
|
||||
self.autosave_label.setObjectName("imageStudioAutosaveLabel")
|
||||
|
||||
self.pull_images_button = QPushButton("拉取蝦皮主图")
|
||||
self.pull_images_button.setObjectName("imageStudioPullImagesButton")
|
||||
self.open_folder_button = QPushButton("打开项目文件夹")
|
||||
self.open_folder_button.setObjectName("imageStudioOpenFolderButton")
|
||||
self.delete_project_button = QPushButton("删除项目")
|
||||
self.delete_project_button.setObjectName("imageStudioDeleteProjectButton")
|
||||
|
||||
layout.addWidget(QLabel("账号"))
|
||||
layout.addWidget(self.account_combo)
|
||||
layout.addWidget(QLabel("商品ID"))
|
||||
layout.addWidget(self.item_id_edit)
|
||||
layout.addWidget(self.open_project_button)
|
||||
layout.addSpacing(8)
|
||||
layout.addWidget(self.current_project_label, 1)
|
||||
layout.addWidget(self.pull_images_button)
|
||||
layout.addWidget(self.autosave_label)
|
||||
layout.addStretch(1)
|
||||
layout.addWidget(self.pull_images_button)
|
||||
layout.addWidget(self.open_folder_button)
|
||||
layout.addWidget(self.delete_project_button)
|
||||
return panel
|
||||
|
||||
def _build_project_panel(self):
|
||||
@@ -355,7 +355,7 @@ class ImageStudioTab(QWidget):
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(8)
|
||||
|
||||
title = QLabel("商品项目")
|
||||
title = QLabel("商品列表")
|
||||
title.setObjectName("imageStudioSectionTitle")
|
||||
hint = QLabel("点击切换项目,项目会自动保存")
|
||||
hint.setObjectName("imageStudioMutedLabel")
|
||||
@@ -382,7 +382,7 @@ class ImageStudioTab(QWidget):
|
||||
layout.setContentsMargins(0, 0, 0, 0)
|
||||
layout.setSpacing(8)
|
||||
|
||||
self.workspace_empty_label = QLabel("选择账号并输入商品ID,点击「打开商品项目」开始。")
|
||||
self.workspace_empty_label = QLabel("选择店铺并输入商品ID后,点击「拉取蝦皮主图」开始。")
|
||||
self.workspace_empty_label.setObjectName("imageStudioWorkspaceEmptyLabel")
|
||||
self.workspace_empty_label.setWordWrap(True)
|
||||
layout.addWidget(self.workspace_empty_label)
|
||||
@@ -635,6 +635,9 @@ class ImageStudioTab(QWidget):
|
||||
background: #eef6ff;
|
||||
color: #24292f;
|
||||
}
|
||||
#imageStudioDeleteProjectButton {
|
||||
color: #b42318;
|
||||
}
|
||||
QTableWidget#imageStudioOriginalTable,
|
||||
QTableWidget#imageStudioPoolTable,
|
||||
QListWidget#imageStudioMainSelectionList,
|
||||
@@ -650,9 +653,9 @@ class ImageStudioTab(QWidget):
|
||||
)
|
||||
|
||||
def _connect_signals(self):
|
||||
self.open_project_button.clicked.connect(self.open_project)
|
||||
self.pull_images_button.clicked.connect(self.pull_main_images)
|
||||
self.open_folder_button.clicked.connect(self.open_project_folder)
|
||||
self.delete_project_button.clicked.connect(self.delete_current_project)
|
||||
self.project_table.itemSelectionChanged.connect(self._on_project_selection_changed)
|
||||
self.original_table.cellClicked.connect(self._on_original_clicked)
|
||||
self.original_table.cellDoubleClicked.connect(self._on_original_double_clicked)
|
||||
@@ -695,18 +698,18 @@ class ImageStudioTab(QWidget):
|
||||
self.projects = []
|
||||
self._status(f"AI工场项目读取失败:{exc}", "danger")
|
||||
self._fill_project_table()
|
||||
if self.current_project is None and self.projects:
|
||||
self._select_project(self.projects[0].id)
|
||||
elif self.current_project is not None:
|
||||
active_ids = {int(project.id) for project in self.projects}
|
||||
if self.current_project is not None and int(self.current_project.id) in active_ids:
|
||||
self._select_project(self.current_project.id, quiet=True)
|
||||
elif self.projects:
|
||||
self._select_project(self.projects[0].id)
|
||||
else:
|
||||
self._refresh_project_summary()
|
||||
self._clear_current_project()
|
||||
|
||||
def _fill_project_table(self):
|
||||
self.project_table.setRowCount(len(self.projects))
|
||||
for row, project in enumerate(self.projects):
|
||||
values = [
|
||||
f"{project.account_alias} / {project.item_id}",
|
||||
project.account_name or project.account_alias,
|
||||
project.item_id,
|
||||
project.updated_at,
|
||||
@@ -718,38 +721,11 @@ class ImageStudioTab(QWidget):
|
||||
self.project_table.setRowHeight(row, 44)
|
||||
self.project_table.resizeColumnsToContents()
|
||||
|
||||
def open_project(self, checked=False):
|
||||
alias = str(self.account_combo.currentData() or "").strip()
|
||||
item_id = self.item_id_edit.text().strip()
|
||||
if not alias:
|
||||
self._message("账号未选择", "请先在④账号管理添加并选择账号。")
|
||||
return
|
||||
if not item_id:
|
||||
self._message("商品ID不能为空", "请输入要打开的蝦皮商品ID。")
|
||||
return
|
||||
account = accounts.get_account(alias, path=self.db_path, config=self.config)
|
||||
try:
|
||||
project = image_studio.create_or_get_project(
|
||||
account,
|
||||
item_id=item_id,
|
||||
path=self.db_path,
|
||||
)
|
||||
except Exception as exc:
|
||||
self._message("打开商品项目失败", str(exc))
|
||||
self._status(f"打开AI工场商品项目失败:{exc}", "danger")
|
||||
return
|
||||
self.current_project = project
|
||||
self.item_id_edit.setText(project.item_id)
|
||||
self._set_account_combo(project.account_alias)
|
||||
self.refresh_projects()
|
||||
self._select_project(project.id)
|
||||
self._status("AI工场商品项目已打开", "success")
|
||||
|
||||
def pull_main_images(self, checked=False):
|
||||
alias = str(self.account_combo.currentData() or "").strip()
|
||||
item_id = self.item_id_edit.text().strip()
|
||||
if not alias or not item_id:
|
||||
self._message("商品项目未打开", "请先选择账号和商品ID并打开商品项目。")
|
||||
self._message("信息未填写完整", "请先选择店铺并输入商品ID,再拉取蝦皮主图。")
|
||||
return
|
||||
worker = ImageStudioPullImagesWorker(
|
||||
alias,
|
||||
@@ -779,7 +755,7 @@ class ImageStudioTab(QWidget):
|
||||
|
||||
def open_project_folder(self, checked=False):
|
||||
if self.current_project is None:
|
||||
self._message("商品项目未打开", "请先打开一个AI工场商品项目。")
|
||||
self._message("未选择商品", "请先从商品列表选择商品,或拉取蝦皮主图。")
|
||||
return
|
||||
dirs = image_studio.default_project_image_dirs(self.current_project, self.config)
|
||||
try:
|
||||
@@ -791,6 +767,44 @@ class ImageStudioTab(QWidget):
|
||||
return
|
||||
self._status(f"已打开项目文件夹:{opened}", "success")
|
||||
|
||||
def delete_current_project(self, checked=False):
|
||||
project = self.current_project
|
||||
if project is None:
|
||||
self._message("未选择商品", "请先从商品列表选择要删除的项目。")
|
||||
return
|
||||
active_jobs = [
|
||||
job
|
||||
for job in self._list_project_jobs(project.id)
|
||||
if job.status in {"pending", "submitted", "running"}
|
||||
]
|
||||
if active_jobs:
|
||||
self._message(
|
||||
"暂不能删除项目",
|
||||
"该商品还有未完成的图片任务。请先等待任务完成、停止本轮或继续查询任务后再删除。",
|
||||
)
|
||||
return
|
||||
display_name = _project_display_name(project)
|
||||
if not self._confirm(
|
||||
"删除项目",
|
||||
f"确定从AI工场商品列表删除“{display_name}”吗?\n"
|
||||
"不会删除蝦皮商品,也不会删除本地图片。再次拉取同一商品会恢复原项目记录。",
|
||||
):
|
||||
return
|
||||
try:
|
||||
image_studio.soft_delete_project(
|
||||
project.id,
|
||||
reason="用户从AI工场商品列表删除",
|
||||
path=self.db_path,
|
||||
)
|
||||
except Exception as exc:
|
||||
message = diagnostics.redact_log_text(str(exc or "未知错误"))
|
||||
self._message("删除项目失败", f"无法从AI工场商品列表删除:{message}")
|
||||
self._status(f"删除AI工场项目失败:{message}", "danger")
|
||||
return
|
||||
self._clear_current_project()
|
||||
self.refresh_projects()
|
||||
self._status(f"已从AI工场商品列表删除:{display_name}", "success")
|
||||
|
||||
def _on_project_selection_changed(self):
|
||||
items = self.project_table.selectedItems()
|
||||
if not items:
|
||||
@@ -820,7 +834,7 @@ class ImageStudioTab(QWidget):
|
||||
self._sync_project_selection(project.id)
|
||||
self._refresh_project_summary()
|
||||
if not quiet:
|
||||
self._status(f"当前AI工场商品项目:{project.account_alias} / {project.item_id}", "muted")
|
||||
self._status(f"当前AI工场商品:{_project_display_name(project)}", "muted")
|
||||
|
||||
def _sync_project_selection(self, project_id):
|
||||
for row in range(self.project_table.rowCount()):
|
||||
@@ -1068,17 +1082,42 @@ class ImageStudioTab(QWidget):
|
||||
|
||||
def _refresh_project_summary(self):
|
||||
if self.current_project is None:
|
||||
self.current_project_label.setText("未打开商品项目")
|
||||
self.autosave_label.setText("选择账号并输入商品ID后打开")
|
||||
self.workspace_empty_label.setText("选择账号并输入商品ID,点击「打开商品项目」开始。")
|
||||
self.current_project_label.setText("未选择商品")
|
||||
self.current_project_label.setToolTip("")
|
||||
self.autosave_label.setText("选择店铺并输入商品ID后拉取")
|
||||
self.workspace_empty_label.setText("选择店铺并输入商品ID后,点击「拉取蝦皮主图」开始。")
|
||||
self._update_project_action_buttons()
|
||||
return
|
||||
alias = self.current_project.account_alias
|
||||
item_id = self.current_project.item_id
|
||||
self.current_project_label.setText(f"{alias} · {item_id}")
|
||||
self.current_project_label.setText(_project_display_name(self.current_project))
|
||||
self.current_project_label.setToolTip(f"账号别名:{self.current_project.account_alias}")
|
||||
self.autosave_label.setText("项目已自动保存")
|
||||
self.workspace_empty_label.setText(
|
||||
f"当前商品:{alias} / {item_id}。按顺序拉取蝦皮主图、选择源图、生成并拖入终选。"
|
||||
f"当前商品:{_project_display_name(self.current_project)}。按顺序拉取蝦皮主图、选择源图、生成并拖入终选。"
|
||||
)
|
||||
self._update_project_action_buttons()
|
||||
|
||||
def _clear_current_project(self):
|
||||
self.current_project = None
|
||||
self.assets = []
|
||||
self.jobs = []
|
||||
self.selections = []
|
||||
self.selected_source_asset_id = None
|
||||
self.item_id_edit.clear()
|
||||
self.prompt_edit.blockSignals(True)
|
||||
try:
|
||||
self.prompt_edit.clear()
|
||||
finally:
|
||||
self.prompt_edit.blockSignals(False)
|
||||
self._fill_original_table()
|
||||
self._fill_pool_table()
|
||||
self._refresh_selection_labels()
|
||||
self._refresh_source_label()
|
||||
self._refresh_project_summary()
|
||||
|
||||
def _update_project_action_buttons(self):
|
||||
enabled = self.current_project is not None and not self._operation_running
|
||||
self.open_folder_button.setEnabled(enabled)
|
||||
self.delete_project_button.setEnabled(enabled)
|
||||
|
||||
def _update_generation_action_text(self, *args):
|
||||
count = self.count_spin.value() if hasattr(self, "count_spin") else 0
|
||||
@@ -1217,7 +1256,7 @@ class ImageStudioTab(QWidget):
|
||||
|
||||
def start_generation(self, checked=False):
|
||||
if self.current_project is None:
|
||||
self._message("商品项目未打开", "请先打开一个AI工场商品项目。")
|
||||
self._message("未选择商品", "请先从商品列表选择商品,或拉取蝦皮主图。")
|
||||
return
|
||||
source = self._asset_by_id(self.selected_source_asset_id)
|
||||
if source is None:
|
||||
@@ -1260,7 +1299,7 @@ class ImageStudioTab(QWidget):
|
||||
|
||||
def resume_generation_jobs(self, checked=False):
|
||||
if self.current_project is None:
|
||||
self._message("商品项目未打开", "请先打开一个AI工场商品项目。")
|
||||
self._message("未选择商品", "请先从商品列表选择商品,或拉取蝦皮主图。")
|
||||
return
|
||||
resumable = image_studio.list_resumable_jobs(
|
||||
path=self.db_path,
|
||||
@@ -1289,7 +1328,7 @@ class ImageStudioTab(QWidget):
|
||||
|
||||
def export_selections(self, checked=False):
|
||||
if self.current_project is None:
|
||||
self._message("商品项目未打开", "请先打开一个AI工场商品项目。")
|
||||
self._message("未选择商品", "请先从商品列表选择商品,或拉取蝦皮主图。")
|
||||
return
|
||||
main_count = len(self._selection_asset_ids("main"))
|
||||
detail_count = len(self._selection_asset_ids("detail"))
|
||||
@@ -1430,9 +1469,8 @@ class ImageStudioTab(QWidget):
|
||||
self._running_worker = None
|
||||
|
||||
def _set_running(self, running):
|
||||
self.open_project_button.setEnabled(not running)
|
||||
self._operation_running = bool(running)
|
||||
self.pull_images_button.setEnabled(not running)
|
||||
self.open_folder_button.setEnabled(not running)
|
||||
self.project_table.setEnabled(not running)
|
||||
self.original_table.setEnabled(not running)
|
||||
self.pool_table.setEnabled(not running)
|
||||
@@ -1451,6 +1489,7 @@ class ImageStudioTab(QWidget):
|
||||
self.resume_button.setEnabled(not running)
|
||||
self.stop_button.setEnabled(running)
|
||||
self.export_button.setEnabled(not running)
|
||||
self._update_project_action_buttons()
|
||||
|
||||
def _set_account_combo(self, alias):
|
||||
index = self.account_combo.findData(alias)
|
||||
@@ -1586,6 +1625,13 @@ def _asset_badge(kind):
|
||||
}.get(str(kind or ""), str(kind or "图片"))
|
||||
|
||||
|
||||
def _project_display_name(project):
|
||||
account_name = str(getattr(project, "account_name", "") or "").strip()
|
||||
account_alias = str(getattr(project, "account_alias", "") or "").strip()
|
||||
item_id = str(getattr(project, "item_id", "") or "").strip()
|
||||
return f"{account_name or account_alias or '未命名店铺'} · {item_id or '未填写商品ID'}"
|
||||
|
||||
|
||||
def _asset_status_text(asset):
|
||||
status = str(getattr(asset, "status", "") or "")
|
||||
local_path = str(getattr(asset, "local_path", "") or "")
|
||||
|
||||
+21
-2
@@ -3,7 +3,7 @@ id: T-606
|
||||
title: AI工场项目入口、商品列表与安全删除优化
|
||||
phase: 7
|
||||
deps: [T-605]
|
||||
status: TODO
|
||||
status: DONE
|
||||
created: 2026-07-11
|
||||
---
|
||||
|
||||
@@ -99,4 +99,23 @@ git diff --check
|
||||
|
||||
## 执行记录
|
||||
|
||||
- 待执行。
|
||||
- 2026-07-11:已完成。
|
||||
- 代码:
|
||||
- `app/gui/tabs/image_studio.py`:移除可见的「打开商品项目」入口;顶部改为店铺/商品ID输入、当前「店铺名 · 商品ID」、紧邻的「拉取蝦皮主图」以及「打开项目文件夹」「删除项目」操作组。
|
||||
- `app/gui/tabs/image_studio.py`:左侧改为「商品列表」,展示店铺、商品ID、更新时间三列;当前项目标题优先显示店铺名,账号别名仅作为 tooltip。
|
||||
- `app/gui/tabs/image_studio.py`:新增「删除项目」确认和软删除入口,明确不删除蝦皮商品或本地图片;删除前拦截 `pending/submitted/running` 任务,删除后显式清理当前项目 UI 并切换到剩余项目或空状态。
|
||||
- `app/gui/tabs/image_studio.py`:统一未选择项目时的中文提示,移除旧按钮依赖,并让项目文件夹/删除按钮随当前项目和后台运行状态正确启用或禁用。
|
||||
- 测试:
|
||||
- `tests/test_gui.py`:更新 AI工场构建测试,覆盖去除旧入口、三列商品列表、顶部中文空状态和项目操作按钮状态。
|
||||
- `tests/test_gui.py`:新增确认取消/确认软删除后切换项目测试,以及未完成图片任务阻止删除测试。
|
||||
- 验证:
|
||||
- 主工作区定向验证通过:
|
||||
- `py -3.10 -m unittest tests.test_gui.GuiTests.test_image_studio_tab_builds_project_pool_and_template_controls tests.test_gui.GuiTests.test_image_studio_deletes_selected_project_only_after_safe_confirmation tests.test_gui.GuiTests.test_image_studio_blocks_deletion_with_active_remote_job tests.test_gui.GuiTests.test_image_studio_pull_main_images_uses_current_item_input tests.test_gui.GuiTests.test_image_studio_worker_thread_is_kept_until_thread_finished`
|
||||
- `python -m ruff check app tests main.py`
|
||||
- `py -3.10 -m compileall app main.py`
|
||||
- `git diff --check -- app/gui/tabs/image_studio.py tests/test_gui.py docs/tasks/T-606.md`
|
||||
- 主工作区存在未提交默认提示词文件改动;为隔离无关改动,在临时干净 worktree `D:\chengma\cmshopee-t606-verify` 应用本任务 diff 后通过:
|
||||
- `python -m ruff check app tests main.py`
|
||||
- `py -3.10 -m compileall app main.py`
|
||||
- `py -3.10 -m unittest discover -s tests`(380 tests)
|
||||
- `git diff --check`
|
||||
|
||||
+94
-8
@@ -494,7 +494,7 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
prompts_dir = os.path.join(temp_dir, "prompts", "image_studio")
|
||||
accounts.create_account("主店", "alias-a", debug_port=9222, config=cfg)
|
||||
account = accounts.create_account("主店", "alias-a", debug_port=9222, config=cfg)
|
||||
prompts.save_image_studio_template("工场模板", "完整提示词", prompts_dir)
|
||||
|
||||
tab = ImageStudioTab(
|
||||
@@ -512,19 +512,25 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
"导入本地图片",
|
||||
" ".join(button.text() for button in tab.findChildren(gui.QPushButton)),
|
||||
)
|
||||
self.assertEqual("打开商品项目", tab.open_project_button.text())
|
||||
self.assertNotIn(
|
||||
"打开商品项目",
|
||||
" ".join(button.text() for button in tab.findChildren(gui.QPushButton)),
|
||||
)
|
||||
self.assertEqual("拉取蝦皮主图", tab.pull_images_button.text())
|
||||
self.assertEqual("删除项目", tab.delete_project_button.text())
|
||||
self.assertEqual("导出到文件夹", tab.export_button.text())
|
||||
self.assertIn("可部分导出", tab.export_hint_label.text())
|
||||
self.assertIn("文件名按终选顺序连续", tab.export_hint_label.text())
|
||||
self.assertEqual("未打开商品项目", tab.current_project_label.text())
|
||||
self.assertIn("打开商品项目", tab.workspace_empty_label.text())
|
||||
self.assertEqual("未选择商品", tab.current_project_label.text())
|
||||
self.assertIn("拉取蝦皮主图", tab.workspace_empty_label.text())
|
||||
self.assertFalse(tab.open_folder_button.isEnabled())
|
||||
self.assertFalse(tab.delete_project_button.isEnabled())
|
||||
self.assertEqual("生成 4 张主图", tab.start_button.text())
|
||||
tab.count_spin.setValue(3)
|
||||
tab.job_type_combo.setCurrentIndex(tab.job_type_combo.findData("detail"))
|
||||
self.assertEqual("生成 3 张详情图", tab.start_button.text())
|
||||
label_texts = "\n".join(label.text() for label in tab.findChildren(gui.QLabel))
|
||||
self.assertIn("商品项目", label_texts)
|
||||
self.assertIn("商品列表", label_texts)
|
||||
self.assertIn("蝦皮原主图", label_texts)
|
||||
self.assertIn("照片池", label_texts)
|
||||
self.assertIn("生成设置", label_texts)
|
||||
@@ -536,15 +542,26 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
tab.load_selected_template()
|
||||
self.assertEqual("完整提示词", tab.prompt_edit.toPlainText())
|
||||
|
||||
tab.item_id_edit.setText("51100639510")
|
||||
tab.open_project()
|
||||
project = image_studio.create_or_get_project(
|
||||
account,
|
||||
item_id="51100639510",
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tab.refresh_projects()
|
||||
self.assertIsNotNone(tab.current_project)
|
||||
self.assertEqual("alias-a", tab.current_project.account_alias)
|
||||
self.assertEqual("51100639510", tab.current_project.item_id)
|
||||
self.assertIn("alias-a", tab.current_project_label.text())
|
||||
self.assertEqual(project.id, tab.current_project.id)
|
||||
self.assertIn("主店", tab.current_project_label.text())
|
||||
self.assertIn("51100639510", tab.current_project_label.text())
|
||||
self.assertEqual("项目已自动保存", tab.autosave_label.text())
|
||||
self.assertEqual(1, tab.project_table.rowCount())
|
||||
self.assertEqual(["店铺", "商品ID", "更新时间"], [
|
||||
tab.project_table.horizontalHeaderItem(column).text()
|
||||
for column in range(tab.project_table.columnCount())
|
||||
])
|
||||
self.assertTrue(tab.open_folder_button.isEnabled())
|
||||
self.assertTrue(tab.delete_project_button.isEnabled())
|
||||
|
||||
original = image_studio.sync_original_asset_urls(
|
||||
tab.current_project.id,
|
||||
@@ -565,6 +582,75 @@ class GuiTests(TempDirMixin, unittest.TestCase):
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_image_studio_deletes_selected_project_only_after_safe_confirmation(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
db.init_db(cfg["db_path"])
|
||||
first = image_studio.create_or_get_project(
|
||||
account_alias="alias-a",
|
||||
account_name="主店",
|
||||
account_slug="alias_a",
|
||||
item_id="51100639510",
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
second = image_studio.create_or_get_project(
|
||||
account_alias="alias-a",
|
||||
account_name="主店",
|
||||
account_slug="alias_a",
|
||||
item_id="51100639511",
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
tab = ImageStudioTab(config=cfg, db_path=cfg["db_path"])
|
||||
self.addCleanup(tab.close)
|
||||
tab._select_project(first.id)
|
||||
|
||||
confirmations = []
|
||||
tab._confirm = lambda title, text: confirmations.append((title, text)) or False
|
||||
tab.delete_current_project()
|
||||
self.assertIsNotNone(image_studio.get_project(first.id, path=cfg["db_path"]))
|
||||
self.assertEqual(first.id, tab.current_project.id)
|
||||
self.assertIn("不会删除蝦皮商品", confirmations[0][1])
|
||||
self.assertIn("不会删除本地图片", confirmations[0][1])
|
||||
|
||||
tab._confirm = lambda title, text: True
|
||||
tab.delete_current_project()
|
||||
self.assertIsNone(image_studio.get_project(first.id, path=cfg["db_path"]))
|
||||
deleted = image_studio.get_project(first.id, path=cfg["db_path"], include_deleted=True)
|
||||
self.assertIsNotNone(deleted)
|
||||
self.assertEqual("用户从AI工场商品列表删除", deleted.deleted_reason)
|
||||
self.assertIsNotNone(tab.current_project)
|
||||
self.assertEqual(second.id, tab.current_project.id)
|
||||
self.assertEqual(1, tab.project_table.rowCount())
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_image_studio_blocks_deletion_with_active_remote_job(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
db.init_db(cfg["db_path"])
|
||||
project = image_studio.create_or_get_project(
|
||||
account_alias="alias-a",
|
||||
account_name="主店",
|
||||
account_slug="alias_a",
|
||||
item_id="51100639510",
|
||||
path=cfg["db_path"],
|
||||
)
|
||||
image_studio.create_job(project.id, path=cfg["db_path"])
|
||||
tab = ImageStudioTab(config=cfg, db_path=cfg["db_path"])
|
||||
self.addCleanup(tab.close)
|
||||
tab._select_project(project.id)
|
||||
messages = []
|
||||
tab._message = lambda title, text: messages.append((title, text))
|
||||
tab._confirm = lambda title, text: self.fail("有未完成任务时不应出现删除确认")
|
||||
|
||||
tab.delete_current_project()
|
||||
|
||||
self.assertIsNotNone(image_studio.get_project(project.id, path=cfg["db_path"]))
|
||||
self.assertEqual("暂不能删除项目", messages[0][0])
|
||||
self.assertIn("未完成的图片任务", messages[0][1])
|
||||
|
||||
self.assert_removed(temp_dir)
|
||||
|
||||
def test_image_studio_final_selection_order_and_guards(self):
|
||||
with self.make_temp_dir() as temp_dir:
|
||||
cfg = self.make_config(temp_dir)
|
||||
|
||||
Reference in New Issue
Block a user