From 5666086c6e01262ff4ce8b9bf9daa54fe14f5b44 Mon Sep 17 00:00:00 2001 From: umsangdon Date: Fri, 4 Sep 2026 22:42:57 +0900 Subject: [PATCH] =?UTF-8?q?fix(B03):=20=ED=8C=8C=EC=9D=BC=20=EA=B5=90?= =?UTF-8?q?=EC=B2=B4=20=ED=9B=84=20=EB=8B=A4=EC=8B=9C=20=EC=97=85=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=ED=95=A0=20=EC=88=98=20=EC=9E=88=EA=B2=8C=20=E2=80=94?= =?UTF-8?q?=20=EB=81=9D=EB=82=9C=20=EC=B9=B4=EB=93=9C=20[=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=84=A0=ED=83=9D]=20+=20=ED=99=95=EC=A0=95=20?= =?UTF-8?q?=EB=8C=80=EA=B8=B0=20=EA=B4=80=EB=AC=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 완료 카드에서도 [파일 선택]을 보임. 올리는 중에만 감춤. 교체 확인 모달과 교체 업로드 규칙은 이미 있던 것을 그대로 씀. - 업로드 관문이 전처리 1단계 IN_PROGRESS 를 무조건 「분석 중」으로 읽어, 자동 확정이 보류된 프로젝트는 영영 새 자료를 받지 못했음. 전처리 진행 파일이 awaiting_confirmation·completed·failed 로 끝나 있으면 받도록 고침. 실제로 도는 중(analyzing)이면 그대로 막음. Co-Authored-By: Claude Opus 5 (1M context) --- B03_FileInput/B03_FileInput_UI_Style.css | 5 ++-- common_util/common_util_workflow_state.py | 36 ++++++++++++++++++++++- 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/B03_FileInput/B03_FileInput_UI_Style.css b/B03_FileInput/B03_FileInput_UI_Style.css index 098c1fba..df638aff 100644 --- a/B03_FileInput/B03_FileInput_UI_Style.css +++ b/B03_FileInput/B03_FileInput_UI_Style.css @@ -699,8 +699,9 @@ display: none; } -.b03-file__card--uploading .b03-file__card-select, -.b03-file__card--completed .b03-file__card-select { +/* 올리는 중에만 [파일 선택]을 감춘다. 끝난 카드에도 남겨 두어야 파일 하나를 갈아 + 끼우고 [파일 업로드]로 다시 돌릴 수 있다(2026-09-04 사용자 지시). */ +.b03-file__card--uploading .b03-file__card-select { display: none; } diff --git a/common_util/common_util_workflow_state.py b/common_util/common_util_workflow_state.py index 502c6267..258fd6da 100644 --- a/common_util/common_util_workflow_state.py +++ b/common_util/common_util_workflow_state.py @@ -260,7 +260,41 @@ async def is_analysis_running(cursor: aiomysql.DictCursor, project_id: str) -> b if not row: return False state = row["state"] if isinstance(row, dict) else row[0] - return str(state) == "IN_PROGRESS" + if str(state) != "IN_PROGRESS": + return False + # 자동 확정이 보류되면 1단계는 사용자가 B04에서 모델을 고를 때까지 IN_PROGRESS 로 + # 남는다 — 계산은 이미 끝난 상태다. 그 사이에도 새 자료는 받아야 한다 + # (2026-09-04 사용자 지시 — 같은 프로젝트로 전처리를 되풀이 시험). + return not await _surface_run_settled(cursor, project_id) + + +# 계산이 끝나 사용자의 다음 조작을 기다리는 진행 단계 — 도는 중이 아니다. +_SETTLED_SURFACE_STAGES = frozenset({"awaiting_confirmation", "completed", "failed"}) + + +async def _surface_run_settled(cursor: aiomysql.DictCursor, project_id: str) -> bool: + """전처리 진행 파일이 「끝났음」으로 적혀 있는가. 파일이 없으면 판단하지 않는다.""" + from pathlib import Path + + from B04_PreProcess.B04_PreProcess_Router_Progress import read_surface_progress + from common_util.common_util_storage import resolve_stored_project_path + + await cursor.execute( + "SELECT storage_path FROM projects WHERE id = %s AND deleted_at IS NULL", + (project_id,), + ) + row = await cursor.fetchone() + if not row: + return False + storage_path = row["storage_path"] if isinstance(row, dict) else row[0] + if not storage_path: + return False + try: + progress = read_surface_progress(Path(resolve_stored_project_path(str(storage_path)))) + except (OSError, ValueError): + return False + stage = str((progress or {}).get("current_stage") or "") + return stage in _SETTLED_SURFACE_STAGES async def reset_stages_after_input_change(cursor: aiomysql.DictCursor, project_id: str) -> None: