diff --git a/README.md b/README.md index c3a8010..f3812e7 100644 --- a/README.md +++ b/README.md @@ -23,11 +23,11 @@ ### 下载与校验 1. 升级前先关闭所有正在运行的 Log Analysis 窗口。 -2. 下载 [Log Analysis v0.1.0-beta.3 安装程序](https://github.com/Reyppp/log-analysis/releases/download/v0.1.0-beta.3/Log-Analysis-Setup-v0.1.0-beta.3-x64.exe) 和同一版本的 [SHA256SUMS.txt](https://github.com/Reyppp/log-analysis/releases/download/v0.1.0-beta.3/SHA256SUMS.txt)。也可以先查看[完整版本说明](https://github.com/Reyppp/log-analysis/releases/tag/v0.1.0-beta.3)。 +2. 下载 [Log Analysis v0.1.0-beta.4 安装程序](https://github.com/Reyppp/log-analysis/releases/download/v0.1.0-beta.4/Log-Analysis-Setup-v0.1.0-beta.4-x64.exe) 和同一版本的 [SHA256SUMS.txt](https://github.com/Reyppp/log-analysis/releases/download/v0.1.0-beta.4/SHA256SUMS.txt)。也可以先查看[完整版本说明](https://github.com/Reyppp/log-analysis/releases/tag/v0.1.0-beta.4)。 3. 在下载目录打开 PowerShell,运行: ```powershell -Get-FileHash -Algorithm SHA256 .\Log-Analysis-Setup-v0.1.0-beta.3-x64.exe +Get-FileHash -Algorithm SHA256 .\Log-Analysis-Setup-v0.1.0-beta.4-x64.exe Get-Content .\SHA256SUMS.txt ``` @@ -35,7 +35,7 @@ Get-Content .\SHA256SUMS.txt ### 安装与首次使用 -1. 双击 `Log-Analysis-Setup-v0.1.0-beta.3-x64.exe`。 +1. 双击 `Log-Analysis-Setup-v0.1.0-beta.4-x64.exe`。 2. 按安装向导继续;程序默认安装到当前用户目录,不需要管理员权限,桌面快捷方式可选。 3. 当前版本是未签名测试版。若 SmartScreen 显示“Windows 已保护你的电脑”,请先确认 SHA-256 已匹配,再选择“更多信息”,核对文件名后选择“仍要运行”。如果组织安全策略禁止运行,请联系管理员,不要关闭系统安全功能。 4. 安装完成后,从开始菜单打开 **Log Analysis**。 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 8d8d80d..6f2513a 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,6 +1,6 @@ -# Log Analysis v0.1.0-beta.3 +# Log Analysis v0.1.0-beta.4 -文件夹选择与性能稳定性测试版,使用 Windows 原生资源管理器选择炉次目录。 +桌面启动稳定性修复版。 ## 主要功能 @@ -14,10 +14,10 @@ ## 本版改进 -- 优化逐列数值校验,黄金样本首次分析时间由约 9 秒降低至约 5.3 秒。 -- 增加分析进度、会话缓存、异常阈值快速重算和重复分析保护。 -- 修复窗口关闭后的残留进程,并限制为单实例运行。 -- 文件夹选择改用 Windows 原生资源管理器界面;取消选择不会清空已有路径。 +- 修复关闭软件后首次重新启动可能无响应的问题。 +- 桌面窗口改为显式注册本地 API,避免扫描 WebView2 原生对象。 +- 保留单实例、原生文件夹选择、会话缓存和分析进度。 +- 25 项测试通过;黄金样本继续识别 243 层,20 次连续启停无残留主进程。 ## 系统要求 diff --git a/desktop.py b/desktop.py index a979acb..58a81ac 100644 --- a/desktop.py +++ b/desktop.py @@ -57,8 +57,8 @@ def acquire_single_instance() -> int | None: class DesktopApi: - def __init__(self) -> None: - self.window = None + def __init__(self, window) -> None: + self._window = window def get_version(self) -> dict[str, str]: return {"name": APP_NAME, "version": APP_VERSION, "channel": BUILD_CHANNEL} @@ -72,7 +72,7 @@ def choose_folder(self) -> dict[str, object]: previous = server.saved_path() directory = previous if Path(previous).is_dir() else str(Path.home()) try: - selected = self.window.create_file_dialog(webview.FileDialog.FOLDER, directory=directory) + selected = self._window.create_file_dialog(webview.FileDialog.FOLDER, directory=directory) path, cancelled, changed = server.apply_folder_selection(selected[0] if selected else "") return {"path": path, "cancelled": cancelled, "changed": changed} except Exception as exc: @@ -90,7 +90,7 @@ def save_export(self, kind: str, threshold: float = 3.5) -> dict[str, object]: if result is None: return {"ok": False, "error": "请先完成炉次分析。"} name = "layer_summary.csv" if kind == "summary" else "anomalies.csv" - selected = self.window.create_file_dialog( + selected = self._window.create_file_dialog( webview.FileDialog.SAVE, directory=str(Path.home() / "Downloads"), save_filename=name, @@ -147,23 +147,23 @@ def main() -> None: worker = threading.Thread(target=httpd.serve_forever, name="log-analysis-http", daemon=True) worker.start() LOGGER.info("local service ready port=%s", httpd.server_port) - api = DesktopApi() url = f"http://127.0.0.1:{httpd.server_port}/?token={quote(token)}" - api.window = webview.create_window( + window = webview.create_window( APP_NAME, url, - js_api=api, width=1440, height=900, min_size=(960, 640), text_select=True, ) + api = DesktopApi(window) + window.expose(api.get_version, api.open_releases, api.choose_folder, api.save_export) def page_loaded() -> None: ready.set() LOGGER.info("window loaded") - api.window.events.loaded += page_loaded + window.events.loaded += page_loaded def watch_startup() -> None: if not ready.wait(15) and not stopped.is_set(): diff --git a/installer/LogAnalysis.iss b/installer/LogAnalysis.iss index 6e6e3d0..10904f4 100644 --- a/installer/LogAnalysis.iss +++ b/installer/LogAnalysis.iss @@ -1,6 +1,6 @@ #define MyAppName "Log Analysis" #ifndef MyAppVersion - #define MyAppVersion "0.1.0-beta.3" + #define MyAppVersion "0.1.0-beta.4" #endif #define MyNumericVersion "0.1.0.0" #define MyAppPublisher "Reyppp" diff --git a/site/index.html b/site/index.html index b88fd48..0678a59 100644 --- a/site/index.html +++ b/site/index.html @@ -38,14 +38,14 @@

单炉次镀膜日志分析

自动关联逐层理论、光学与设备数据,定位需复核层。全程本地运行,原始日志只读。

- 下载 v0.1.0-beta.3 - SHA-256 校验文件 - 版本说明 + 下载 v0.1.0-beta.4 + SHA-256 校验文件 + 版本说明
运行方式
Windows 10/11 x64
数据边界
本地只读,不上传
-
当前版本
v0.1.0-beta.3
+
当前版本
v0.1.0-beta.4
@@ -173,20 +173,20 @@

重点复核层

-

安装 Windows 测试版

当前版本未进行代码签名,适用于功能验证。已知问题:部分环境下单层详情图表或原始截图可能不显示,其他全炉分析页面不受影响。

+

安装 Windows 测试版

v0.1.0-beta.4 修复关闭后首次重新启动可能无响应的问题。当前版本未进行代码签名。已知问题:部分环境下单层详情图表或原始截图可能不显示。

  1. 1
    关闭旧版本

    升级前先退出所有正在运行的 Log Analysis 窗口。

  2. -
  3. 2
    下载两个文件

    同时下载 Log-Analysis-Setup-v0.1.0-beta.3-x64.exe 和 SHA256SUMS.txt。

  4. -
  5. 3
    核对 SHA-256

    在下载目录运行 Get-FileHash -Algorithm SHA256 .\Log-Analysis-Setup-v0.1.0-beta.3-x64.exe,与校验文件比较。

  6. +
  7. 2
    下载两个文件

    同时下载 Log-Analysis-Setup-v0.1.0-beta.4-x64.exe 和 SHA256SUMS.txt。

  8. +
  9. 3
    核对 SHA-256

    在下载目录运行 Get-FileHash -Algorithm SHA256 .\Log-Analysis-Setup-v0.1.0-beta.4-x64.exe,与校验文件比较。

  10. 4
    运行当前用户安装

    双击安装程序;不需要管理员权限,桌面快捷方式可选。

  11. 5
    处理测试版提示

    哈希匹配后,如 SmartScreen 警告可选择“更多信息”并核对文件名;组织策略阻止时请联系管理员。

  12. 6
    选择炉次并分析

    从开始菜单打开应用,点击“选择文件夹”,再点击“开始分析”。

diff --git a/test_analyzer.py b/test_analyzer.py index a9798cb..10c43d7 100644 --- a/test_analyzer.py +++ b/test_analyzer.py @@ -145,10 +145,10 @@ def test_desktop_uses_native_explorer_folder_dialog(self): with tempfile.TemporaryDirectory() as folder: selected = str(Path(folder).resolve()) calls = [] - api = desktop.DesktopApi() - api.window = SimpleNamespace( + window = SimpleNamespace( create_file_dialog=lambda dialog, **kwargs: calls.append((dialog, kwargs)) or (selected,) ) + api = desktop.DesktopApi(window) with patch.object(server, "saved_path", return_value=selected), patch.object(server, "save_path"), patch.dict( server.STATE, {"path": selected, "result": object()}, clear=True ): @@ -158,6 +158,34 @@ def test_desktop_uses_native_explorer_folder_dialog(self): self.assertFalse(result["cancelled"]) self.assertFalse(result["changed"]) + def test_desktop_api_exposes_only_callable_methods(self): + window = SimpleNamespace() + api = desktop.DesktopApi(window) + public = {name: getattr(api, name) for name in dir(api) if not name.startswith("_")} + self.assertEqual( + set(public), + {"choose_folder", "get_version", "open_releases", "save_export"}, + ) + self.assertTrue(all(callable(value) for value in public.values())) + self.assertFalse(hasattr(api, "window")) + self.assertIs(api._window, window) + + def test_desktop_api_version_release_and_export(self): + with tempfile.TemporaryDirectory() as folder: + output = Path(folder) / "layer_summary.csv" + window = SimpleNamespace(create_file_dialog=lambda *_args, **_kwargs: (str(output),)) + api = desktop.DesktopApi(window) + self.assertEqual(api.get_version()["version"], desktop.APP_VERSION) + with patch.object(desktop.webbrowser, "open", return_value=True) as open_browser: + self.assertTrue(api.open_releases()) + open_browser.assert_called_once_with(desktop.RELEASES_URL) + result = SimpleNamespace(layer_summary=pd.DataFrame({"layer": [1]})) + with patch.dict(server.STATE, {"result": result}, clear=True): + saved = api.save_export("summary") + self.assertTrue(saved["ok"]) + self.assertEqual(saved["path"], str(output)) + self.assertTrue(output.read_bytes().startswith(b"\xef\xbb\xbf")) + def test_progress_callback_reaches_completion(self): with tempfile.TemporaryDirectory() as folder: root = Path(folder) @@ -195,7 +223,7 @@ def test_health_endpoint_identifies_service(self): body = response.read().decode("utf-8") self.assertIn('"service": "coating-analyzer"', body) self.assertIn('"app_name": "Log Analysis"', body) - self.assertIn('"app_version": "0.1.0-beta.3"', body) + self.assertIn(f'"app_version": "{desktop.APP_VERSION}"', body) finally: httpd.shutdown() httpd.server_close() @@ -317,18 +345,19 @@ def test_public_site_is_local_only_and_links_to_releases(self): html = (project / "site" / "index.html").read_text(encoding="utf-8") readme = (project / "README.md").read_text(encoding="utf-8") release_notes = (project / "RELEASE_NOTES.md").read_text(encoding="utf-8") + version = desktop.APP_VERSION installer_url = ( - "https://github.com/Reyppp/log-analysis/releases/download/v0.1.0-beta.3/" - "Log-Analysis-Setup-v0.1.0-beta.3-x64.exe" + f"https://github.com/Reyppp/log-analysis/releases/download/v{version}/" + f"Log-Analysis-Setup-v{version}-x64.exe" ) checksum_url = ( - "https://github.com/Reyppp/log-analysis/releases/download/v0.1.0-beta.3/" + f"https://github.com/Reyppp/log-analysis/releases/download/v{version}/" "SHA256SUMS.txt" ) self.assertIn("数据只在本机处理", html) self.assertIn(installer_url, html) self.assertIn(checksum_url, html) - self.assertIn("https://github.com/Reyppp/log-analysis/releases/tag/v0.1.0-beta.3", html) + self.assertIn(f"https://github.com/Reyppp/log-analysis/releases/tag/v{version}", html) self.assertIn(installer_url, readme) self.assertIn("Get-FileHash -Algorithm SHA256", readme) for content in (html, readme, release_notes): diff --git a/version.py b/version.py index 6295b6b..d608dbb 100644 --- a/version.py +++ b/version.py @@ -1,5 +1,5 @@ APP_NAME = "Log Analysis" -APP_VERSION = "0.1.0-beta.3" +APP_VERSION = "0.1.0-beta.4" BUILD_CHANNEL = "beta" REPOSITORY_URL = "https://github.com/Reyppp/log-analysis" RELEASES_URL = f"{REPOSITORY_URL}/releases"