fix(B03): LAS 없이 설계 업로드가 지형 PRJ·TFW 필수 검사에 막히던 것 해소
- 「LAS 없이 설계」면 지형 한 벌(포인트클라우드·지형 PRJ·TFW)을 필수에서 면제 - 업로드 요청 검사도 완료 검사와 같은 기준으로 노선 PRJ 와 지형 PRJ 를 갈라 셈 (종전에는 확장자만 봐서 노선 PRJ 를 지형 PRJ 로 오인) - 업로드 현황의 「필수 충족」은 stage 0 완료를 우선 근거로 판정 — B04 이동 버튼 복구 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,9 @@ from B03_FileInput.B03_FileInput_Router_Helpers import (
|
||||
from B03_FileInput.B03_FileInput_Router_Helpers import (
|
||||
_write_stage_metadata as _write_stage_metadata,
|
||||
)
|
||||
from B03_FileInput.B03_FileInput_Router_Helpers import (
|
||||
upload_file_types as upload_file_types,
|
||||
)
|
||||
from B03_FileInput.B03_FileInput_Schema import (
|
||||
FileUploadDescriptor,
|
||||
FileUploadResponse,
|
||||
@@ -156,12 +159,13 @@ async def upload_project_files(
|
||||
"message": "LAS 없이 설계를 켠 상태에서는 LAS/LAZ 파일을 올릴 수 없습니다.",
|
||||
},
|
||||
)
|
||||
if not las_free and las_count != 1:
|
||||
# 지형 파일은 도엽별로 여러 장이 올 수 있다 — 합쳐서 전처리한다(2026-09-06 사용자 확정).
|
||||
if not las_free and las_count < 1:
|
||||
return JSONResponse(
|
||||
status_code=400,
|
||||
content={
|
||||
"status": "error",
|
||||
"message": "LAS 또는 LAZ 파일을 정확히 1개 포함해야 합니다.",
|
||||
"message": "LAS 또는 LAZ 파일을 1개 이상 포함해야 합니다.",
|
||||
},
|
||||
)
|
||||
# 계획노선은 shapefile 또는 CSV 한 벌이다 (2026-08-31) — 문구도 그렇게 맞춘다
|
||||
@@ -175,7 +179,7 @@ async def upload_project_files(
|
||||
"message": "계획노선 파일(shapefile 의 .shp 또는 .csv)을 정확히 1개 포함해야 합니다.",
|
||||
},
|
||||
)
|
||||
request_file_types = {Path(filename).suffix.lower().lstrip(".") for filename in filenames}
|
||||
request_file_types = upload_file_types(filenames)
|
||||
missing_required = _missing_required_file_types(request_file_types, las_free)
|
||||
if missing_required:
|
||||
return JSONResponse(
|
||||
@@ -350,8 +354,10 @@ async def get_project_upload_overview(
|
||||
)
|
||||
for row in sessions
|
||||
],
|
||||
required_complete=_REQUIRED_FILE_TYPES <= file_types
|
||||
and (point_cloud_id is not None or stage0_complete),
|
||||
# stage 0 을 마쳤으면 서버 필수검사를 이미 통과한 것이다 — LAS 없이 설계는
|
||||
# 지형 한 벌(prj·tfw)이 아예 없으므로 여기서 다시 세면 B04 이동이 막힌다.
|
||||
required_complete=stage0_complete
|
||||
or (_REQUIRED_FILE_TYPES <= file_types and point_cloud_id is not None),
|
||||
analysis_complete=analysis_complete,
|
||||
)
|
||||
except Exception:
|
||||
|
||||
@@ -62,14 +62,34 @@ def _is_point_cloud_result(result: UploadedFileResult) -> bool:
|
||||
return result.file_type.lower() in _POINT_CLOUD_FILE_TYPES
|
||||
|
||||
|
||||
def upload_file_types(filenames: list[str]) -> set[str]:
|
||||
"""업로드 요청의 파일명을 **완료 검사와 같은 기준**으로 유형화한다.
|
||||
|
||||
확장자만 세면 노선 세트의 `.prj`가 지형 PRJ로 오인돼, 요청 검사는 통과하고 저장 뒤
|
||||
완료 검사에서 누락으로 갈린다(2026-09-06 실측 — LAS 없이 설계가 이 어긋남으로 막혔다).
|
||||
노선 도형(`.shp`)과 basename이 같은 PRJ는 화면의 카드 배정과 같은 규칙으로 `route_prj`.
|
||||
"""
|
||||
route_stem = next(
|
||||
(Path(name).stem for name in filenames if Path(name).suffix.lower() == ".shp"), None
|
||||
)
|
||||
types: set[str] = set()
|
||||
for name in filenames:
|
||||
suffix = Path(name).suffix.lower().lstrip(".")
|
||||
if suffix == "prj" and route_stem is not None and Path(name).stem == route_stem:
|
||||
suffix = "route_prj"
|
||||
types.add(suffix)
|
||||
return types
|
||||
|
||||
|
||||
def _missing_required_file_types(file_types: set[str], las_free: bool = False) -> list[str]:
|
||||
missing = sorted(_REQUIRED_FILE_TYPES - file_types)
|
||||
# LAS 없는 설계(도엽등고선 기반, 2026-08-30)는 지형 한 벌(포인트클라우드·지형 PRJ·
|
||||
# TFW)을 통째로 받지 않는다 — 화면도 그 카드들을 필수에서 뺀다.
|
||||
missing = [] if las_free else sorted(_REQUIRED_FILE_TYPES - file_types)
|
||||
if not file_types.intersection(_ROUTE_FILE_TYPES):
|
||||
missing.append("csv/shp")
|
||||
# shapefile로 왔으면 형제 파일이 다 있어야 노선을 읽는다.
|
||||
if "shp" in file_types:
|
||||
missing.extend(sorted(_SHAPEFILE_REQUIRED_TYPES - file_types))
|
||||
# LAS 없는 설계(도엽등고선 기반, 2026-08-30)는 LAS 필수를 면제한다.
|
||||
if not las_free and not file_types.intersection(_POINT_CLOUD_FILE_TYPES):
|
||||
missing.append("las/laz")
|
||||
return missing
|
||||
@@ -143,6 +163,15 @@ async def _complete_file_input_if_ready(
|
||||
# 아직 다시 만들어지지 않은 뒷단계 화면이 옛 결과를 새 자료 것인 양 보여준다.
|
||||
stored_path = await get_project_storage_relative_path(connection, project_id)
|
||||
project_root = Path(resolve_stored_project_path(stored_path))
|
||||
# 지형 파일 여러 장은 합쳐서 전처리한다 — 다른 사업지 파일이 섞이면 합친 범위가
|
||||
# 통째로 어긋나므로 여기서 막는다(2026-09-06 사용자 지시). 머리글만 읽어 즉시 끝난다.
|
||||
from B04_PreProcess.B04_PreProcess_Engine_Structurize import merge_gap_error
|
||||
from B04_PreProcess.B04_PreProcess_Repository import list_project_point_cloud_paths
|
||||
|
||||
terrain_paths = await list_project_point_cloud_paths(connection, project_id, project_root)
|
||||
gap_message = merge_gap_error(terrain_paths)
|
||||
if gap_message:
|
||||
raise ValueError(gap_message)
|
||||
clear_designing(project_root)
|
||||
discard_initial_snapshot(project_root)
|
||||
await purge_project_outputs(connection, str(project_id), project_root)
|
||||
|
||||
Reference in New Issue
Block a user