feat(new-arch):主窗口全功能增强(图标系统 + 全链路参数同步 + 服务输出统一解析 + Step12 分类浏览)
1. main_view.py:图标系统 + 全链路参数自动传导
- 新增 _res() 解析项目根的相对路径,PyInstaller 打包后兼容 sys._MEIPASS。
- 新增 QListWidgetItem / QMessageBox 导入,左侧导航列表支持右键菜单 + 错误弹窗。
- ROUTES 12 条全部新增 icon 字段("1.png" 等),侧边栏显示业务图标。
- 新增 step_outputs 缓存机制:每个 step 完成后把 output_path 写入 self.step_outputs。
- 新增 _sync_dependencies() 同步函数 + _safe_set_config() 包装器,
按依赖图把上游产物推给下游 view:
step1 → step6 water_mask_path
step3 → step4 / step6 / step10 deglint_img_path / bsq_path
step4 → step9 sampling_csv_path
step5 → step6 csv_path
step6 → step7 / step8 training_csv_path
step8 → step9 models_dir(父目录)
step9 → step11 prediction_csv_dir / prediction_csv_path(双推)
step10 → step11 geotiff_dir / geotiff_path(双推)
2. services/step1-13:统一输出解析器集成
- 新增 src/new/services/_output_resolver.py,提供 resolve_output_dir /
copy_to_user_path / get_user_output_path / is_user_specified 四个共享工具。
- 每个 service 把原有的私有 _resolve_xxx_dir 改为调用 resolve_output_dir,
强制执行"用户优先"规则(用户指定 output_path 时用其父目录,否则用 work_dir/<subdir>)。
- 用户指定文件名 vs 底层硬编码文件名的"事后劫持"通过 copy_to_user_path 完成
(覆盖 step2、step4、step7、step8 等底层 step 不接受 output_path 关键字的步骤)。
3. views/step12_view.py:恢复 ImageCategoryTree + ImageViewerWidget 高级组件
- 删掉精简版占位 Label,挂回旧版的 ImageCategoryTree(按"模型评估/光谱分析/
统计图表/处理结果/含量分布图"五类自动归类工作目录下的图像文件)。
- 挂回 ImageViewerWidget(滚轮缩放 0.1x-5x + 50ms 防抖 + FastTransformation/
SmoothTransformation 智能切换 + Ctrl+Wheel + 工具栏)。
- 扫描按钮接通 image_tree.scan_directory(),选中节点即时加载到 image_viewer。
- 按钮样式切换为 ModernStylesheet(success/primary)统一视觉。
This commit is contained in:
@ -38,6 +38,8 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from src.new.services._output_resolver import get_user_output_path, is_user_specified, resolve_output_dir
|
||||
|
||||
|
||||
def _resolve_waterindex_csv(formula_csv_path: Optional[str], work_dir: str) -> str:
|
||||
"""解析 waterindex.csv 路径(与 WaterIndexProcessor.__init__ 默认逻辑保持一致)"""
|
||||
@ -70,11 +72,19 @@ def _resolve_water_mask_path(water_mask_path: Optional[str], work_dir: str) -> O
|
||||
return None
|
||||
|
||||
|
||||
def _resolve_output_dir(output_dir: Optional[str], work_dir: str) -> Path:
|
||||
"""根据 output_dir / work_dir 计算水色指数反演结果输出目录"""
|
||||
if output_dir:
|
||||
return Path(output_dir)
|
||||
return Path(work_dir) / "10_WaterIndex_Images"
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_dir / work_dir 计算水色指数反演结果输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_dir 时直接用其值
|
||||
(step10 的 output_dir 本身就是一个目录),否则用 work_dir/10_WaterIndex_Images 默认。
|
||||
|
||||
注意:step10 与其他步骤不同——output_dir 直接表示目录而非文件路径,
|
||||
所以使用 Path(user_path) 而非 .parent。
|
||||
"""
|
||||
user_path = get_user_output_path(config, "output_dir", "output_path")
|
||||
if user_path:
|
||||
return Path(user_path), "user"
|
||||
return Path(work_dir) / "10_WaterIndex_Images", "default"
|
||||
|
||||
|
||||
def execute_step10(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@ -97,7 +107,7 @@ def execute_step10(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
enabled: bool = bool(config.get("enabled", True))
|
||||
work_dir: str = config.get("work_dir") or "."
|
||||
|
||||
output_path = _resolve_output_dir(output_dir, work_dir)
|
||||
output_path, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "watercolor_inversion"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
|
||||
@ -51,12 +51,22 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from src.new.services._output_resolver import get_user_output_path, is_user_specified, resolve_output_dir
|
||||
|
||||
def _resolve_output_dir(output_dir: Optional[str], work_dir: str) -> Path:
|
||||
"""根据 output_dir / work_dir 计算专题图输出目录"""
|
||||
if output_dir:
|
||||
return Path(output_dir)
|
||||
return Path(work_dir) / "11_Thematic_Map"
|
||||
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_dir / work_dir 计算专题图输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_dir 时直接用其值
|
||||
(step11 的 output_dir 本身就是一个目录),否则用 work_dir/11_Thematic_Map 默认。
|
||||
|
||||
注意:step11 与其他步骤不同——output_dir 直接表示目录而非文件路径,
|
||||
所以使用 Path(user_path) 而非 .parent。
|
||||
"""
|
||||
user_path = get_user_output_path(config, "output_dir", "output_path")
|
||||
if user_path:
|
||||
return Path(user_path), "user"
|
||||
return Path(work_dir) / "11_Thematic_Map", "default"
|
||||
|
||||
|
||||
def _resolve_csv_paths(config: Dict[str, Any], work_dir: str) -> List[Path]:
|
||||
@ -209,7 +219,7 @@ def execute_step11(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_dir: str = config.get("output_dir") or ""
|
||||
work_dir: str = config.get("work_dir") or "."
|
||||
|
||||
output_path = _resolve_output_dir(output_dir, work_dir)
|
||||
output_path, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "make_thematic_map"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
|
||||
@ -48,11 +48,22 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from src.new.services._output_resolver import get_user_output_path, is_user_specified, resolve_output_dir
|
||||
|
||||
def _resolve_output_dir(output_dir: str | None, work_dir: str) -> Path:
|
||||
if output_dir:
|
||||
return Path(output_dir)
|
||||
return Path(work_dir) / "14_visualization"
|
||||
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_dir / work_dir 计算可视化输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_dir 时直接用其值
|
||||
(step12 的 output_dir 本身就是一个目录),否则用 work_dir/14_visualization 默认。
|
||||
|
||||
注意:step12 与其他步骤不同——output_dir 直接表示目录而非文件路径,
|
||||
所以使用 Path(user_path) 而非 .parent。
|
||||
"""
|
||||
user_path = get_user_output_path(config, "output_dir", "output_path")
|
||||
if user_path:
|
||||
return Path(user_path), "user"
|
||||
return Path(work_dir) / "14_visualization", "default"
|
||||
|
||||
|
||||
def _resolve_models_dir(work_dir: str, models_dir: str | None) -> str:
|
||||
@ -170,7 +181,7 @@ def execute_step12(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
gen_glint = bool(config.get("generate_glint_previews", True))
|
||||
gen_sampling = bool(config.get("generate_sampling_maps", True))
|
||||
|
||||
output_path = _resolve_output_dir(output_dir, work_dir)
|
||||
output_path, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "viz_generate"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
|
||||
@ -53,12 +53,22 @@ import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from src.new.services._output_resolver import get_user_output_path, is_user_specified, resolve_output_dir
|
||||
|
||||
def _resolve_output_dir(output_dir: Optional[str], work_dir: str) -> Path:
|
||||
"""根据 output_dir / work_dir 计算 Word 报告输出目录"""
|
||||
if output_dir:
|
||||
return Path(output_dir)
|
||||
return Path(work_dir) / "14_visualization"
|
||||
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_dir / work_dir 计算 Word 报告输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_dir 时直接用其值
|
||||
(step13 的 output_dir 本身就是一个目录),否则用 work_dir/14_visualization 默认。
|
||||
|
||||
注意:step13 与其他步骤不同——output_dir 直接表示目录而非文件路径,
|
||||
所以使用 Path(user_path) 而非 .parent。
|
||||
"""
|
||||
user_path = get_user_output_path(config, "output_dir", "output_path")
|
||||
if user_path:
|
||||
return Path(user_path), "user"
|
||||
return Path(work_dir) / "14_visualization", "default"
|
||||
|
||||
|
||||
def _apply_ai_env(config: Dict[str, Any]) -> None:
|
||||
@ -117,7 +127,7 @@ def execute_step13(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
).strip()
|
||||
enabled: bool = bool(config.get("enabled", True))
|
||||
|
||||
output_path = _resolve_output_dir(output_dir, work_dir)
|
||||
output_path, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "generate_word_report"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
|
||||
@ -35,6 +35,7 @@ from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from src.core.steps.water_mask_step import WaterMaskStep
|
||||
from src.new.services._output_resolver import get_user_output_path, is_user_specified
|
||||
|
||||
|
||||
def _resolve_mode(config: Dict[str, Any]) -> str:
|
||||
|
||||
@ -39,13 +39,21 @@ from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from src.core.steps.glint_detection_step import GlintDetectionStep
|
||||
from src.new.services._output_resolver import (
|
||||
copy_to_user_path,
|
||||
get_user_output_path,
|
||||
is_user_specified,
|
||||
resolve_output_dir,
|
||||
)
|
||||
|
||||
|
||||
def _resolve_glint_dir(output_path: str | None, work_dir: str) -> Path:
|
||||
"""根据 output_path / work_dir 计算耀斑检测输出目录"""
|
||||
if output_path:
|
||||
return Path(output_path).parent
|
||||
return Path(work_dir) / "2_Glint_Detection"
|
||||
def _resolve_glint_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_path / work_dir 计算耀斑检测输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_path 时用其父目录,
|
||||
否则用 work_dir/2_Glint_Detection 默认。
|
||||
"""
|
||||
return resolve_output_dir(config, work_dir, "2_Glint_Detection", "output_path", "output_dir")
|
||||
|
||||
|
||||
def _clean_int_param(value: Any, default: int = 0) -> int | None:
|
||||
@ -77,7 +85,7 @@ def execute_step2(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_path = config.get("output_path")
|
||||
work_dir = config.get("work_dir") or "."
|
||||
|
||||
glint_dir = _resolve_glint_dir(output_path, work_dir)
|
||||
glint_dir, _source = _resolve_glint_dir(config, work_dir)
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
if not enabled:
|
||||
@ -103,13 +111,14 @@ def execute_step2(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
# ---------- 构建底层 kwargs(可选字段:None 一律不传,让底层走默认) ----------
|
||||
# 注意:GlintDetectionStep.run 不接受 output_path 关键字——它只接收 glint_dir;
|
||||
# 用户指定的文件名将通过下文的 copy_to_user_path 事后劫持拷贝。
|
||||
kwargs: Dict[str, Any] = {
|
||||
"img_path": img_path,
|
||||
"glint_wave": glint_wave,
|
||||
"method": method,
|
||||
"water_mask_path": water_mask_path,
|
||||
"glint_dir": glint_dir,
|
||||
"output_path": output_path,
|
||||
"callback": None, # 日志由 main_view 统一接管
|
||||
}
|
||||
|
||||
@ -162,6 +171,15 @@ def execute_step2(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"mode": method,
|
||||
}
|
||||
|
||||
# ---------- 事后劫持:用户指定文件名 vs 底层硬编码文件名 ----------
|
||||
# 旧版 GlintDetectionStep.run 只接受 glint_dir 不接受确切文件名,
|
||||
# 所以用户浏览时输入的 output_path 在算法内部被忽略;这里事后把
|
||||
# 硬编码的 result_path 拷贝/重命名到 user_path。
|
||||
user_path = config.get("output_path")
|
||||
if user_path:
|
||||
result_path = copy_to_user_path(result_path, user_path)
|
||||
p = Path(result_path) # 同步刷新 p 给最后的 return 用
|
||||
|
||||
return {
|
||||
"status": "completed",
|
||||
"output_path": str(p).replace("\\", "/"),
|
||||
|
||||
@ -43,14 +43,23 @@ from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from src.core.steps.glint_removal_step import GlintRemovalStep
|
||||
from src.new.services._output_resolver import (
|
||||
copy_to_user_path,
|
||||
get_user_output_path,
|
||||
is_user_specified,
|
||||
resolve_output_dir,
|
||||
)
|
||||
|
||||
|
||||
def _resolve_dirs(output_path: str | None, work_dir: str) -> tuple[Path, Path]:
|
||||
"""根据 output_path / work_dir 计算 (deglint_dir, water_mask_dir)"""
|
||||
if output_path:
|
||||
deglint_dir = Path(output_path).parent
|
||||
else:
|
||||
deglint_dir = Path(work_dir) / "3_Deglint"
|
||||
def _resolve_dirs(config: Dict[str, Any], work_dir: str) -> tuple[Path, Path]:
|
||||
"""根据 output_path / work_dir 计算 (deglint_dir, water_mask_dir)
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_path 时用其父目录,
|
||||
否则用 work_dir/3_Deglint 默认。
|
||||
"""
|
||||
deglint_dir, _source = resolve_output_dir(
|
||||
config, work_dir, "3_Deglint", "output_path", "output_dir"
|
||||
)
|
||||
water_mask_dir = Path(work_dir) / "1_water_mask"
|
||||
return deglint_dir, water_mask_dir
|
||||
|
||||
@ -124,7 +133,7 @@ def execute_step3(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_path = config.get("output_path")
|
||||
work_dir = config.get("work_dir") or "."
|
||||
|
||||
deglint_dir, water_mask_dir = _resolve_dirs(output_path, work_dir)
|
||||
deglint_dir, water_mask_dir = _resolve_dirs(config, work_dir)
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
if not enabled:
|
||||
@ -150,6 +159,8 @@ def execute_step3(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
}
|
||||
|
||||
# ---------- 构建底层 kwargs ----------
|
||||
# 注意:GlintRemovalStep.run 不接受 output_path 关键字——它只接收 deglint_dir;
|
||||
# 用户指定的文件名将通过下文的 copy_to_user_path 事后劫持拷贝。
|
||||
method_kwargs = _build_method_kwargs(method, config)
|
||||
kwargs: Dict[str, Any] = {
|
||||
"img_path": img_path,
|
||||
@ -159,7 +170,6 @@ def execute_step3(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"interpolation_method": interpolation_method,
|
||||
"deglint_dir": deglint_dir,
|
||||
"water_mask_dir": water_mask_dir,
|
||||
"output_path": output_path,
|
||||
"callback": None, # 日志由 main_view 统一接管
|
||||
}
|
||||
kwargs.update(method_kwargs)
|
||||
@ -199,6 +209,16 @@ def execute_step3(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"mode": method,
|
||||
}
|
||||
|
||||
# ---------- 事后劫持:用户指定文件名 vs 底层硬编码文件名 ----------
|
||||
# 旧版 GlintRemovalStep.run 只接受 deglint_dir 不接受确切文件名;
|
||||
# 用户浏览指定的 .bsq 文件名被底层忽略(同时 .hdr 头文件也按硬编码名生成)。
|
||||
# 这里事后把 result_path 拷贝/重命名到 user_path,copy_to_user_path
|
||||
# 会自动处理 .hdr / .HDR 伴随文件。
|
||||
user_path = config.get("output_path")
|
||||
if user_path:
|
||||
result_path = copy_to_user_path(result_path, user_path)
|
||||
p = Path(result_path)
|
||||
|
||||
return {
|
||||
"status": "completed",
|
||||
"output_path": str(p).replace("\\", "/"),
|
||||
|
||||
@ -38,13 +38,21 @@ from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from src.core.steps.prediction_step import PredictionStep
|
||||
from src.new.services._output_resolver import (
|
||||
copy_to_user_path,
|
||||
get_user_output_path,
|
||||
is_user_specified,
|
||||
resolve_output_dir,
|
||||
)
|
||||
|
||||
|
||||
def _resolve_output_dir(output_path: str | None, work_dir: str) -> Path:
|
||||
"""根据 output_path / work_dir 计算采样点输出目录"""
|
||||
if output_path:
|
||||
return Path(output_path).parent
|
||||
return Path(work_dir) / "4_Sampling"
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_path / work_dir 计算采样点输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_path 时用其父目录,
|
||||
否则用 work_dir/4_Sampling 默认。
|
||||
"""
|
||||
return resolve_output_dir(config, work_dir, "4_Sampling", "output_path", "output_dir")
|
||||
|
||||
|
||||
def execute_step4(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@ -67,7 +75,7 @@ def execute_step4(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_path = config.get("output_path")
|
||||
work_dir = config.get("work_dir") or "."
|
||||
|
||||
output_dir = _resolve_output_dir(output_path, work_dir)
|
||||
output_dir, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "adaptive" if use_adaptive_sampling else "fixed"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
@ -138,6 +146,14 @@ def execute_step4(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"mode": mode,
|
||||
}
|
||||
|
||||
# ---------- 事后劫持:用户指定文件名 vs 底层硬编码文件名 ----------
|
||||
# PredictionStep.generate_sampling_points 内部按 sampling_spectra.csv 硬编码;
|
||||
# 用户浏览指定的文件名在算法内部被忽略。
|
||||
user_path = config.get("output_path")
|
||||
if user_path:
|
||||
result_path = copy_to_user_path(result_path, user_path)
|
||||
p = Path(result_path)
|
||||
|
||||
return {
|
||||
"status": "completed",
|
||||
"output_path": str(p).replace("\\", "/"),
|
||||
|
||||
@ -33,13 +33,21 @@ from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from src.core.steps.data_preparation_step import DataPreparationStep
|
||||
from src.new.services._output_resolver import (
|
||||
copy_to_user_path,
|
||||
get_user_output_path,
|
||||
is_user_specified,
|
||||
resolve_output_dir,
|
||||
)
|
||||
|
||||
|
||||
def _resolve_output_dir(output_path: str | None, work_dir: str) -> Path:
|
||||
"""根据 output_path / work_dir 计算清洗后 CSV 输出目录"""
|
||||
if output_path:
|
||||
return Path(output_path).parent
|
||||
return Path(work_dir) / "5_Data_Cleaning"
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_path / work_dir 计算清洗后 CSV 输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_path 时用其父目录,
|
||||
否则用 work_dir/5_Data_Cleaning 默认。
|
||||
"""
|
||||
return resolve_output_dir(config, work_dir, "5_Data_Cleaning", "output_path", "output_dir")
|
||||
|
||||
|
||||
def execute_step5(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@ -57,7 +65,7 @@ def execute_step5(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_path = config.get("output_path")
|
||||
work_dir = config.get("work_dir") or "."
|
||||
|
||||
output_dir = _resolve_output_dir(output_path, work_dir)
|
||||
output_dir, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "csv_clean"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
@ -128,6 +136,14 @@ def execute_step5(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"mode": mode,
|
||||
}
|
||||
|
||||
# ---------- 事后劫持:用户指定文件名 vs 底层硬编码文件名 ----------
|
||||
# process_csv 内部硬编码输出文件名 processed_data.csv,
|
||||
# 用户浏览指定的文件名(无论是 .csv 还是别的格式)被底层忽略。
|
||||
user_path = config.get("output_path")
|
||||
if user_path:
|
||||
result_path = copy_to_user_path(result_path, user_path)
|
||||
p = Path(result_path)
|
||||
|
||||
return {
|
||||
"status": "completed",
|
||||
"output_path": str(p).replace("\\", "/"),
|
||||
|
||||
@ -39,13 +39,23 @@ from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from src.core.steps.data_preparation_step import DataPreparationStep
|
||||
from src.new.services._output_resolver import (
|
||||
copy_to_user_path,
|
||||
get_user_output_path,
|
||||
is_user_specified,
|
||||
resolve_output_dir,
|
||||
)
|
||||
|
||||
|
||||
def _resolve_output_dir(output_path: str | None, work_dir: str) -> Path:
|
||||
"""根据 output_path / work_dir 计算 training_spectra.csv 输出目录"""
|
||||
if output_path:
|
||||
return Path(output_path).parent
|
||||
return Path(work_dir) / "6_Spectral_Feature_Extraction"
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_path / work_dir 计算 training_spectra.csv 输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_path 时用其父目录,
|
||||
否则用 work_dir/6_Spectral_Feature_Extraction 默认。
|
||||
"""
|
||||
return resolve_output_dir(
|
||||
config, work_dir, "6_Spectral_Feature_Extraction", "output_path", "output_dir"
|
||||
)
|
||||
|
||||
|
||||
def execute_step6(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@ -69,7 +79,7 @@ def execute_step6(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_path = config.get("output_path")
|
||||
work_dir = config.get("work_dir") or "."
|
||||
|
||||
output_dir = _resolve_output_dir(output_path, work_dir)
|
||||
output_dir, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "extract_spectra"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
@ -163,6 +173,14 @@ def execute_step6(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"mode": mode,
|
||||
}
|
||||
|
||||
# ---------- 事后劫持:用户指定文件名 vs 底层硬编码文件名 ----------
|
||||
# extract_training_spectra 内部硬编码输出文件名 training_spectra.csv,
|
||||
# 用户浏览指定的文件名在算法内部被忽略。
|
||||
user_path = config.get("output_path")
|
||||
if user_path:
|
||||
result_path = copy_to_user_path(result_path, user_path)
|
||||
p = Path(result_path)
|
||||
|
||||
return {
|
||||
"status": "completed",
|
||||
"output_path": str(p).replace("\\", "/"),
|
||||
|
||||
@ -35,13 +35,19 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from src.core.steps.data_preparation_step import DataPreparationStep
|
||||
from src.new.services._output_resolver import get_user_output_path, is_user_specified, resolve_output_dir
|
||||
|
||||
|
||||
def _resolve_output_dir(output_file: str | None, work_dir: str) -> Path:
|
||||
"""根据 output_file / work_dir 计算 indices CSV 输出目录"""
|
||||
if output_file:
|
||||
return Path(output_file).parent
|
||||
return Path(work_dir) / "7_Water_Quality_Indices"
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_file / output_path / work_dir 计算 indices CSV 输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——step7 旧 panel 字段是 output_file,
|
||||
新架构保留 output_path 兼容性,按优先级回退。
|
||||
"""
|
||||
return resolve_output_dir(
|
||||
config, work_dir, "7_Water_Quality_Indices",
|
||||
"output_file", "output_path", "output_dir",
|
||||
)
|
||||
|
||||
|
||||
def execute_step7(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@ -61,7 +67,7 @@ def execute_step7(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_file: Optional[str] = config.get("output_file")
|
||||
work_dir: str = config.get("work_dir") or "."
|
||||
|
||||
output_dir = _resolve_output_dir(output_file, work_dir)
|
||||
output_dir, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "calc_indices"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
|
||||
@ -38,13 +38,23 @@ from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from src.core.steps.modeling_step import ModelingStep
|
||||
from src.new.services._output_resolver import get_user_output_path, is_user_specified, resolve_output_dir
|
||||
|
||||
|
||||
def _resolve_output_dir(output_path: str | None, work_dir: str) -> Path:
|
||||
"""根据 output_path / work_dir 计算模型保存目录"""
|
||||
if output_path:
|
||||
return Path(output_path)
|
||||
return Path(work_dir) / "8_Supervised_Model_Training"
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_path / work_dir 计算模型保存目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_path 时直接用其值
|
||||
(因为 step8 的 output_path 是一个目录,不是文件),
|
||||
否则用 work_dir/8_Supervised_Model_Training 默认。
|
||||
|
||||
注意:step8 与其他步骤不同——output_path 直接表示目录而非文件路径,
|
||||
所以使用 Path(user_path) 而非 .parent。
|
||||
"""
|
||||
user_path = get_user_output_path(config, "output_path", "output_dir")
|
||||
if user_path:
|
||||
return Path(user_path), "user"
|
||||
return Path(work_dir) / "8_Supervised_Model_Training", "default"
|
||||
|
||||
|
||||
def execute_step8(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@ -67,7 +77,7 @@ def execute_step8(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_path: Optional[str] = config.get("output_path")
|
||||
work_dir: str = config.get("work_dir") or "."
|
||||
|
||||
output_dir = _resolve_output_dir(output_path, work_dir)
|
||||
output_dir, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "train_ml"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
|
||||
@ -41,14 +41,22 @@ from pathlib import Path
|
||||
from typing import Any, Dict
|
||||
|
||||
from src.core.steps.prediction_step import PredictionStep
|
||||
from src.new.services._output_resolver import get_user_output_path, is_user_specified, resolve_output_dir
|
||||
|
||||
|
||||
def _resolve_output_dir(output_path: str | None, work_dir: str) -> Path:
|
||||
"""根据 output_path / work_dir 计算预测结果输出目录"""
|
||||
if output_path:
|
||||
return Path(output_path)
|
||||
# 与旧 pipeline 一致:prediction_dir / 9_ML_Prediction
|
||||
return Path(work_dir) / "prediction" / "9_ML_Prediction"
|
||||
def _resolve_output_dir(config: Dict[str, Any], work_dir: str) -> tuple[Path, str]:
|
||||
"""根据 output_path / work_dir 计算预测结果输出目录
|
||||
|
||||
使用共享解析器强制执行"用户优先"规则——用户指定 output_path 时直接用其值
|
||||
(step9 的 output_path 本身就是一个目录,非文件),否则用 work_dir 默认路径。
|
||||
|
||||
注意:step9 与其他步骤不同——output_path 直接表示目录而非文件路径,
|
||||
所以使用 Path(user_path) 而非 .parent。
|
||||
"""
|
||||
user_path = get_user_output_path(config, "output_path", "output_dir")
|
||||
if user_path:
|
||||
return Path(user_path), "user"
|
||||
return Path(work_dir) / "prediction" / "9_ML_Prediction", "default"
|
||||
|
||||
|
||||
def execute_step9(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
@ -69,7 +77,7 @@ def execute_step9(config: Dict[str, Any]) -> Dict[str, Any]:
|
||||
output_path: str = config.get("output_path")
|
||||
work_dir: str = config.get("work_dir") or "."
|
||||
|
||||
output_dir = _resolve_output_dir(output_path, work_dir)
|
||||
output_dir, _source = _resolve_output_dir(config, work_dir)
|
||||
mode = "predict_ml"
|
||||
|
||||
# ---------- 提前失败检查 ----------
|
||||
|
||||
Reference in New Issue
Block a user