From df8f364587dd8bb3a7b750b6edf2042c63172879 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Thors=C3=A9n?= <10030664+MickOls@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:08:12 +0200 Subject: [PATCH 1/2] Clean internal refs and fix docs and CI triggers --- .github/workflows/ci.yml | 4 + README.md | 4 +- cosmo_required_fixes.patch | 533 +++++++++++++++++++++ cosmo_required_fixes.patch:Zone.Identifier | Bin 0 -> 25 bytes docs/getting-started/quickstart.md | 11 +- src/cosmo/converters/openlabel_to_omega.py | 4 +- src/cosmo/gui/main_window.py | 2 +- src/cosmo/gui/workers.py | 2 +- tests/cli_helpers.py | 4 +- tests/conftest.py | 4 +- tests/test_calibration_compute_whitebox.py | 8 +- tests/test_console_scripts_smoke.py | 8 +- tests/test_convert_app_e2e_not_smoke.py | 2 +- tests/test_convert_no_scripts_fallback.py | 4 +- tests/test_core_calibrate_whitebox.py | 14 +- tests/test_core_convert_whitebox.py | 12 +- tests/test_integration_mcap_convert_app.py | 10 +- tests/test_integration_mcap_converter.py | 10 +- tests/test_integration_mcap_gt_count.py | 14 +- tests/test_main_dispatch.py | 4 +- tests/test_openlabel_to_omega_whitebox.py | 8 +- 21 files changed, 599 insertions(+), 63 deletions(-) create mode 100644 cosmo_required_fixes.patch create mode 100644 cosmo_required_fixes.patch:Zone.Identifier diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 710b6f8..831b249 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,6 +3,10 @@ name: CI on: push: pull_request: + workflow_dispatch: + schedule: + # Run optional/heavier integration tests weekly and on demand. + - cron: "0 3 * * 1" jobs: test: diff --git a/README.md b/README.md index 8300192..91b1d4b 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,8 @@ python -m pip install -e . --group dev > tools explicitly: `pip install -e . pytest ruff` (adding `betterosi mcap` for > MCAP integration tests). -> Tip 1: The **GUI** is launced with `cosmo` or `cosmo gui`. -> Tip 2: During development with downloaded repro, all `cosmo` commands can be replaced with `python run_cosmo.py ...`, `cosmo.cmd ...` or `cosmo.ps1 ...`. `python run_gui.py` always starts the **GUI**. +> Tip 1: The **GUI** is launched with `cosmo` or `cosmo gui`. +> Tip 2: In headless environments, prefer explicit subcommands such as `cosmo convert ...` rather than plain `cosmo`, which may start the GUI. Convert using [ORBIT](https://github.com/RI-SE/ORBIT) georef as the primary pixel→ground mapping (**Convert** tab in the **GUI**): diff --git a/cosmo_required_fixes.patch b/cosmo_required_fixes.patch new file mode 100644 index 0000000..d118fa4 --- /dev/null +++ b/cosmo_required_fixes.patch @@ -0,0 +1,533 @@ +--- a/.github/workflows/ci.yml ++++ b/.github/workflows/ci.yml +@@ -3,6 +3,10 @@ + on: + push: + pull_request: ++ workflow_dispatch: ++ schedule: ++ # Run optional/heavier integration tests weekly and on demand. ++ - cron: "0 3 * * 1" + + jobs: + test: +--- a/README.md ++++ b/README.md +@@ -52,8 +52,8 @@ + > tools explicitly: `pip install -e . pytest ruff` (adding `betterosi mcap` for + > MCAP integration tests). + +-> Tip 1: The **GUI** is launced with `cosmo` or `cosmo gui`. +-> Tip 2: During development with downloaded repro, all `cosmo` commands can be replaced with `python run_cosmo.py ...`, `cosmo.cmd ...` or `cosmo.ps1 ...`. `python run_gui.py` always starts the **GUI**. ++> Tip 1: The **GUI** is launched with `cosmo` or `cosmo gui`. ++> Tip 2: In headless environments, prefer explicit subcommands such as `cosmo convert ...` rather than plain `cosmo`, which may start the GUI. + + Convert using [ORBIT](https://github.com/RI-SE/ORBIT) georef as the primary pixel→ground mapping (**Convert** tab in the **GUI**): + +--- a/docs/getting-started/quickstart.md ++++ b/docs/getting-started/quickstart.md +@@ -10,17 +10,16 @@ + cosmo gui + ``` + +-Development shortcut (either of): ++Use the installed console script: + + ```bash +-python run_gui.py # Start GUI +-python run_cosmo.py gui # Same behaviour as cosmo (when installed) +-cosmo.cmd gui # Same behaviour in Windows cmd termial as cosmo (when installed) +-cosmo.ps1 gui # Same behaviour in Windows PowerShell termial as cosmo (when installed) ++cosmo gui ++# or simply: ++cosmo + ``` + --- + ## CLI +-> During development `cosmo ...` can be replaced with `python run_cosmo ...`, `cosmo.exe ...` or `cosmo.ps1 ...` without needing to install cosmo, e.g., `python run_cosmo.py convert ...`. To install for development use `uv sync --group dev` or `pip install -e . --group dev`. ++> For development, install the package first (for example `uv sync --group dev` or `pip install -e . --group dev`) and then use the `cosmo ...` console script. + --- + ### Convert (recommended: ORBIT georef) + +--- a/src/cosmo/converters/openlabel_to_omega.py ++++ b/src/cosmo/converters/openlabel_to_omega.py +@@ -721,7 +721,7 @@ + # ---------------------------- + # IMPORTANT: + # Use betterosi.Writer as a context manager to ensure MCAP footer/summary are finalized properly. [1](https://deepwiki.com/ika-rwth-aachen/omega-prime/3.1-loading-and-saving-recordings)[4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/) +- # Also write topic names without leading "/" to match common omega-prime usage. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py)[2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ # Also write topic names without leading "/" to match common omega-prime usage. + + def _write_map(writer_mcap): + if odr_path and os.path.isfile(odr_path): +@@ -752,7 +752,7 @@ + gt.proj_string = _gt_proj_string + if _gt_country_code: + gt.country_code = _gt_country_code +- # Provide log_time explicitly; readers often build indices from it. [4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/)[3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++ # Provide log_time explicitly; readers often build indices from it. [4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/) + writer_mcap.add(gt, topic="ground_truth", log_time=total_nanos) + + # If MCAP enabled, write with context manager; else only build CSV rows +--- a/src/cosmo/gui/main_window.py ++++ b/src/cosmo/gui/main_window.py +@@ -9,7 +9,7 @@ + from pathlib import Path + from typing import Optional + +-# Qt binding selection: PyQt5 → PySide6 → PyQt6 (same as your current GUI). [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_openlabel_to_omega.py) ++# Qt binding selection: PyQt5 → PySide6 → PyQt6 (same as your current GUI). + try: + from PyQt5 import QtCore, QtGui, QtWidgets + _QT_API = "PyQt5" +--- a/src/cosmo/gui/workers.py ++++ b/src/cosmo/gui/workers.py +@@ -3,7 +3,7 @@ + + from typing import Callable + +-# Qt binding selection (same preference order as your current GUI) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_openlabel_to_omega.py) ++# Qt binding selection (same preference order as your current GUI) + try: + from PyQt5 import QtCore + _Signal = QtCore.pyqtSignal +--- a/tests/cli_helpers.py ++++ b/tests/cli_helpers.py +@@ -21,10 +21,10 @@ + env.setdefault("MPLBACKEND", "Agg") + + if os.name == "nt": +- cmd = [sys.executable, "-m", "cosmo.cli.main", *args] # real entrypoint logic [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ cmd = [sys.executable, "-m", "cosmo.cli.main", *args] # real entrypoint logic + else: + exe = shutil.which("cosmo") +- cmd = [exe, *args] if exe else [sys.executable, "-m", "cosmo.cli.main", *args] # same entrypoint [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ cmd = [exe, *args] if exe else [sys.executable, "-m", "cosmo.cli.main", *args] # same entrypoint + + return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, env=env) + +--- a/tests/conftest.py ++++ b/tests/conftest.py +@@ -16,7 +16,7 @@ + Fallback: if the console script isn't on PATH (rare in CI), run via module entrypoint: + python -m cosmo.cli.main + +- NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI. + """ + env = dict(os.environ) + env.setdefault("MPLBACKEND", "Agg") # CI already sets this, but keep it deterministic. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) +@@ -26,7 +26,7 @@ + cmd = [exe, *args] + else: + # Still the real entrypoint, just executed as a module. +- cmd = [os.environ.get("PYTHON", "python"), "-m", "cosmo.cli.main", *args] # dispatch logic in main.py [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ cmd = [os.environ.get("PYTHON", "python"), "-m", "cosmo.cli.main", *args] # dispatch logic in main.py + + return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, env=env) + +--- a/tests/test_calibration_compute_whitebox.py ++++ b/tests/test_calibration_compute_whitebox.py +@@ -6,7 +6,7 @@ + import numpy as np + import pytest + +-from cosmo.calibration.compute import ( # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++from cosmo.calibration.compute import ( # + compute_calibration, + write_calibration_outputs, + ) +@@ -75,7 +75,7 @@ + fps=30.0, + image_width=3840, + image_height=2160, +- residuals_png_path=str(resid_png), # optional; may become None on plot errors [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++ residuals_png_path=str(resid_png), # optional; may become None on plot errors + overlay_png_path=None, + image_path=None, + openlabel_path=None, +@@ -85,7 +85,7 @@ + assert Path(outs.summary_json_path).is_file() + + calib = json.loads(Path(outs.calibration_json_path).read_text(encoding="utf-8")) +- # Required keys written by write_calibration_outputs [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++ # Required keys written by write_calibration_outputs + for k in ("fps", "image_width", "image_height", "homography", "intrinsics", "extrinsics", "default_dimensions_m"): + assert k in calib + H = calib["homography"] +@@ -95,7 +95,7 @@ + for k in ("rmse_m", "inliers_count", "pairs_used", "pixel_points", "world_points_ENU_m", "homography"): + assert k in summary + +- # Residual plot can be omitted if matplotlib errors; if present, ensure file exists [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++ # Residual plot can be omitted if matplotlib errors; if present, ensure file exists + if outs.residuals_png_path is not None: + assert Path(outs.residuals_png_path).is_file() + +--- a/tests/test_console_scripts_smoke.py ++++ b/tests/test_console_scripts_smoke.py +@@ -50,19 +50,19 @@ + assert cosmo_calibrate, "Expected 'cosmo-calibrate' on PATH or in venv bin dir after install." + assert cosmo_gui, "Expected 'cosmo-gui' on PATH or in venv bin dir after install." + +- # Always safe: module entrypoint tests real main.py behavior. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ # Always safe: module entrypoint tests real main.py behavior. + res = _run([sys.executable, "-m", "cosmo.cli.main", "--version"]) + assert res.returncode == 0, f"cosmo --version failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" + assert res.stdout.strip(), "cosmo --version produced no output." + +- # Dispatch help via main.py (convert/calibrate are subcommands). [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ # Dispatch help via main.py (convert/calibrate are subcommands). + res = _run([sys.executable, "-m", "cosmo.cli.main", "convert", "--help"]) + assert res.returncode == 0, f"cosmo convert --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" + + res = _run([sys.executable, "-m", "cosmo.cli.main", "calibrate", "--help"]) + assert res.returncode == 0, f"cosmo calibrate --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" + +- # Direct entrypoints: run via module on Windows to avoid blocked .EXE shims. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)[2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ # Direct entrypoints: run via module on Windows to avoid blocked .EXE shims. + if os.name == "nt": + res = _run([sys.executable, "-m", "cosmo.cli.convert", "--help"]) + assert res.returncode == 0, f"python -m cosmo.cli.convert --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" +@@ -76,4 +76,4 @@ + res = _run([cosmo_calibrate, "--help"]) + assert res.returncode == 0, f"cosmo-calibrate --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" + +- # Do NOT execute cosmo-gui in tests: it launches GUI immediately and can fail headless. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)[3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ # Do NOT execute cosmo-gui in tests: it launches GUI immediately and can fail headless. +--- a/tests/test_convert_app_e2e_not_smoke.py ++++ b/tests/test_convert_app_e2e_not_smoke.py +@@ -5,7 +5,7 @@ + import json + from pathlib import Path + +-from cosmo.app.convert_app import ( # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++from cosmo.app.convert_app import ( # + ConvertConfig, + run_convert, + ) +--- a/tests/test_convert_no_scripts_fallback.py ++++ b/tests/test_convert_no_scripts_fallback.py +@@ -2,7 +2,7 @@ + from __future__ import annotations + + from cosmo.app.convert_app import ( +- _get_converter_or_raise, # internal helper [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++ _get_converter_or_raise, # internal helper + ) + + +@@ -11,7 +11,7 @@ + fn = _get_converter_or_raise() + assert callable(fn) + except RuntimeError as e: +- # If it fails, ensure the error message guides the developer correctly. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++ # If it fails, ensure the error message guides the developer correctly. + msg = str(e) + assert "COSMO converter implementation not found" in msg + assert "cosmo.converters.openlabel_to_omega" in msg +--- a/tests/test_core_calibrate_whitebox.py ++++ b/tests/test_core_calibrate_whitebox.py +@@ -3,7 +3,7 @@ + import json + from pathlib import Path + +-from cosmo.app.calibrate_app import ( # app layer API [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) ++from cosmo.app.calibrate_app import ( # app layer API + CalibrateConfig, + run_calibrate, + ) +@@ -34,7 +34,7 @@ + opendrive = tmp_path / "map.xodr" + opendrive.write_text("", encoding="utf-8") + +- # Deterministic run folder: existing out_dir + run_name => out_dir/run_name [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) ++ # Deterministic run folder: existing out_dir + run_name => out_dir/run_name + runs_dir = tmp_path / "runs" + runs_dir.mkdir() + +@@ -55,7 +55,7 @@ + ) + + logs: list[str] = [] +- result = run_calibrate(cfg, log_fn=logs.append) # same signature used by CLI [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) ++ result = run_calibrate(cfg, log_fn=logs.append) # same signature used by CLI + + run_dir = Path(result.run_dir) + outputs_dir = Path(result.outputs_dir) +@@ -63,7 +63,7 @@ + assert outputs_dir == run_dir / "outputs" + assert outputs_dir.exists() + +- # Output naming is stem-based: _calibration.json etc. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) ++ # Output naming is stem-based: _calibration.json etc. + calib_path = Path(result.calibration_json_path) + assert calib_path.is_file() + assert calib_path.name.endswith("_calibration.json") +@@ -73,15 +73,15 @@ + H = calib["homography"] + assert isinstance(H, list) and len(H) == 3 and all(len(r) == 3 for r in H) + +- # Summary JSON should be produced (unless compute/write changes) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) ++ # Summary JSON should be produced (unless compute/write changes) + if result.summary_json_path: + assert Path(result.summary_json_path).is_file() + +- # Residual plot is intended but can be None (matplotlib issues), so keep optional. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) ++ # Residual plot is intended but can be None (matplotlib issues), so keep optional. + if result.residuals_png_path: + assert Path(result.residuals_png_path).is_file() + +- # No overlay expected because image=None [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) ++ # No overlay expected because image=None + assert result.overlay_png_path is None + + # Some logging should occur (useful diagnostic) +--- a/tests/test_core_convert_whitebox.py ++++ b/tests/test_core_convert_whitebox.py +@@ -4,7 +4,7 @@ + import json + from pathlib import Path + +-from cosmo.app.convert_app import ( # app layer API [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++from cosmo.app.convert_app import ( # app layer API + ConvertConfig, + run_convert, + ) +@@ -31,7 +31,7 @@ + openlabel_path = tmp_path / "openlabel.json" + openlabel_path.write_text(json.dumps(openlabel), encoding="utf-8") + +- # Minimal calibration file path (convert_app passes this to converter if provided) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++ # Minimal calibration file path (convert_app passes this to converter if provided) + calib = { + "fps": 30.0, + "image_width": 3840, +@@ -51,7 +51,7 @@ + calibration=str(calib_path), + fps=None, + write_csv=True, +- write_mcap=False, # keep core tests independent of optional MCAP deps [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++ write_mcap=False, # keep core tests independent of optional MCAP deps + swap_xy=False, + flip_x=False, + flip_y=False, +@@ -62,7 +62,7 @@ + ) + + logs: list[str] = [] +- result = run_convert(cfg, log_fn=logs.append) # same signature used by CLI [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++ result = run_convert(cfg, log_fn=logs.append) # same signature used by CLI + + run_dir = Path(result.run_dir) + outputs_dir = Path(result.outputs_dir) +@@ -70,7 +70,7 @@ + assert outputs_dir == run_dir / "outputs" + assert outputs_dir.exists() + +- # csv_path is only set if the file exists (summary filters non-existent files) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++ # csv_path is only set if the file exists (summary filters non-existent files) + assert result.csv_path is not None + csv_path = Path(result.csv_path) + assert csv_path.is_file() +@@ -103,7 +103,7 @@ + write_mcap=False, + out_dir=str(runs_dir), + run_name="wb_no_csv", +- ) # relies on defaults for other fields [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) ++ ) # relies on defaults for other fields + + result = run_convert(cfg, log_fn=lambda _: None) + assert result.csv_path is None +--- a/tests/test_integration_mcap_convert_app.py ++++ b/tests/test_integration_mcap_convert_app.py +@@ -5,7 +5,7 @@ + + import pytest + +-from cosmo.app.convert_app import ( # [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++from cosmo.app.convert_app import ( # + ConvertConfig, + run_convert, + ) +@@ -20,7 +20,7 @@ + + + @pytest.mark.integration +-@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # + def test_run_convert_app_writes_mcap_and_sets_result_path(tmp_path: Path): + # Minimal OpenLABEL + openlabel = { +@@ -36,7 +36,7 @@ + calib_path = tmp_path / "Calibration.json" + calib_path.write_text(json.dumps({"fps": 10.0, "homography": [[1, 0, 0], [0, 1, 0], [0, 0, 1]]}), encoding="utf-8") + +- # OpenDRIVE embedded flag in convert_app summary is based on cfg.opendrive [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++ # OpenDRIVE embedded flag in convert_app summary is based on cfg.opendrive + odr_path = tmp_path / "map.xodr" + odr_path.write_text("", encoding="utf-8") + +@@ -51,14 +51,14 @@ + write_mcap=True, + out_dir=str(runs_dir), + run_name="int_mcap", +- ) # config matches app layer [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++ ) # config matches app layer + + result = run_convert(cfg, log_fn=None) + + assert Path(result.run_dir).exists() + assert Path(result.outputs_dir).exists() + +- # convert_app only sets mcap_path if file exists [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++ # convert_app only sets mcap_path if file exists + assert result.mcap_path is not None + assert Path(result.mcap_path).is_file() + assert Path(result.mcap_path).stat().st_size > 0 +--- a/tests/test_integration_mcap_converter.py ++++ b/tests/test_integration_mcap_converter.py +@@ -6,7 +6,7 @@ + import pytest + + from cosmo.converters.openlabel_to_omega import ( +- convert_openlabel_to_omega, # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ convert_openlabel_to_omega, # + ) + + +@@ -37,7 +37,7 @@ + + + @pytest.mark.integration +-@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # + def test_converter_writes_mcap_with_ground_truth_and_map(tmp_path: Path): + # Minimal OpenLABEL with two frames (ensures multiple GT messages) + openlabel = { +@@ -67,7 +67,7 @@ + encoding="utf-8", + ) + +- # Provide OpenDRIVE so map embedding path is exercised (ground_truth_map topic) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ # Provide OpenDRIVE so map embedding path is exercised (ground_truth_map topic) + odr_path = tmp_path / "map.xodr" + odr_path.write_text("", encoding="utf-8") + +@@ -82,7 +82,7 @@ + georef_data_path=None, + fps_arg=None, + write_csv=False, # focus on MCAP in this integration test +- write_mcap=True, # requires betterosi [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ write_mcap=True, # requires betterosi + swap_xy=False, + flip_x=False, + flip_y=False, +@@ -98,6 +98,6 @@ + # Optional deeper validation if the `mcap` reader is installed: + topics = _try_read_mcap_topics(mcap_path) + if topics: +- # Converter writes these topics without leading '/' [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ # Converter writes these topics without leading '/' + assert "ground_truth" in topics + assert "ground_truth_map" in topics +--- a/tests/test_integration_mcap_gt_count.py ++++ b/tests/test_integration_mcap_gt_count.py +@@ -23,7 +23,7 @@ + + import pytest + +-from cosmo.app.convert_app import ( # app layer [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++from cosmo.app.convert_app import ( # app layer + ConvertConfig, + run_convert, + ) +@@ -62,7 +62,7 @@ + + + @pytest.mark.integration +-@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; cannot write MCAP") # converter depends on it [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; cannot write MCAP") # converter depends on it + @pytest.mark.skipif(not has_mcap_reader(), reason="mcap reader not installed; cannot inspect MCAP") + def test_mcap_gt_count_map_once_and_strictly_increasing_log_time(tmp_path: Path): + # Create OpenLABEL with a known number of frames. +@@ -104,7 +104,7 @@ + encoding="utf-8", + ) + +- # OpenDRIVE included so map is embedded as `ground_truth_map` (written once with log_time=0) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ # OpenDRIVE included so map is embedded as `ground_truth_map` (written once with log_time=0) + odr_path = tmp_path / "map.xodr" + odr_path.write_text("", encoding="utf-8") + +@@ -119,10 +119,10 @@ + write_mcap=True, + out_dir=str(runs_dir), + run_name="mcap_gt_count", +- ) # app layer writes into per-run outputs/ folder [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++ ) # app layer writes into per-run outputs/ folder + + result = run_convert(cfg, log_fn=None) +- assert result.mcap_path, "Expected mcap_path to be set when MCAP is written" # app layer sets only if file exists [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) ++ assert result.mcap_path, "Expected mcap_path to be set when MCAP is written" # app layer sets only if file exists + + mcap_path = assert_mcap_nonempty(result.mcap_path) + +@@ -130,11 +130,11 @@ + gt_times = _collect_topic_log_times(mcap_path, "ground_truth") + assert len(gt_times) == len(frame_ids), f"Expected {len(frame_ids)} ground_truth messages, got {len(gt_times)}" + +- # 2) Map message written exactly once at log_time=0 (converter writes once) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ # 2) Map message written exactly once at log_time=0 (converter writes once) + map_times = _collect_topic_log_times(mcap_path, "ground_truth_map") + assert len(map_times) == 1, f"Expected exactly 1 ground_truth_map message, got {len(map_times)}" + assert map_times[0] == 0, f"Expected ground_truth_map log_time=0, got {map_times[0]}" + +- # 3) GroundTruth log_time strictly increasing (converter uses log_time=total_nanos) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ # 3) GroundTruth log_time strictly increasing (converter uses log_time=total_nanos) + assert gt_times == sorted(gt_times), f"ground_truth log_time not non-decreasing: {gt_times}" + assert all(b > a for a, b in zip(gt_times, gt_times[1:])), f"ground_truth log_time not strictly increasing: {gt_times}" +--- a/tests/test_main_dispatch.py ++++ b/tests/test_main_dispatch.py +@@ -14,5 +14,5 @@ + text=True, + env={**os.environ, "MPLBACKEND": "Agg"}, + ) +- assert res.returncode == 0 # main.py returns 0 on --version [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) +- assert res.stdout.strip() # prints version string [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) ++ assert res.returncode == 0 # main.py returns 0 on --version ++ assert res.stdout.strip() # prints version string +--- a/tests/test_openlabel_to_omega_whitebox.py ++++ b/tests/test_openlabel_to_omega_whitebox.py +@@ -6,7 +6,7 @@ + + import numpy as np + +-from cosmo.converters.openlabel_to_omega import ( # [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++from cosmo.converters.openlabel_to_omega import ( # + convert_openlabel_to_omega, + load_alignment, + parse_openlabel, +@@ -82,7 +82,7 @@ + georef_data_path=None, + fps_arg=None, + write_csv=True, +- write_mcap=False, # ensure no betterosi dependency [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ write_mcap=False, # ensure no betterosi dependency + swap_xy=False, + flip_x=False, + flip_y=False, +@@ -96,11 +96,11 @@ + + with csv_path.open(newline="", encoding="utf-8") as f: + reader = csv.DictReader(f) +- # Columns defined in module [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ # Columns defined in module + for col in ("total_nanos", "idx", "x", "y", "yaw", "type_name", "subtype_name", "role_name"): + assert col in (reader.fieldnames or []) + rows = list(reader) + assert len(rows) >= 2 +- # Ensure time is non-decreasing (rows are sorted by total_nanos) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) ++ # Ensure time is non-decreasing (rows are sorted by total_nanos) + times = [int(r["total_nanos"]) for r in rows] + assert times == sorted(times) diff --git a/cosmo_required_fixes.patch:Zone.Identifier b/cosmo_required_fixes.patch:Zone.Identifier new file mode 100644 index 0000000000000000000000000000000000000000..d6c1ec682968c796b9f5e9e080cc6f674b57c766 GIT binary patch literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x During development `cosmo ...` can be replaced with `python run_cosmo ...`, `cosmo.exe ...` or `cosmo.ps1 ...` without needing to install cosmo, e.g., `python run_cosmo.py convert ...`. To install for development use `uv sync --group dev` or `pip install -e . --group dev`. +> For development, install the package first (for example `uv sync --group dev` or `pip install -e . --group dev`) and then use the `cosmo ...` console script. --- ### Convert (recommended: ORBIT georef) diff --git a/src/cosmo/converters/openlabel_to_omega.py b/src/cosmo/converters/openlabel_to_omega.py index d4e4cb9..baf713b 100644 --- a/src/cosmo/converters/openlabel_to_omega.py +++ b/src/cosmo/converters/openlabel_to_omega.py @@ -721,7 +721,7 @@ def _collect_all_dims() -> Dict[str, Tuple[float, float, float]]: # ---------------------------- # IMPORTANT: # Use betterosi.Writer as a context manager to ensure MCAP footer/summary are finalized properly. [1](https://deepwiki.com/ika-rwth-aachen/omega-prime/3.1-loading-and-saving-recordings)[4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/) - # Also write topic names without leading "/" to match common omega-prime usage. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py)[2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + # Also write topic names without leading "/" to match common omega-prime usage. def _write_map(writer_mcap): if odr_path and os.path.isfile(odr_path): @@ -752,7 +752,7 @@ def _write_ground_truth(writer_mcap, t_sec: float, total_nanos: int, moving_obje gt.proj_string = _gt_proj_string if _gt_country_code: gt.country_code = _gt_country_code - # Provide log_time explicitly; readers often build indices from it. [4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/)[3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) + # Provide log_time explicitly; readers often build indices from it. [4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/) writer_mcap.add(gt, topic="ground_truth", log_time=total_nanos) # If MCAP enabled, write with context manager; else only build CSV rows diff --git a/src/cosmo/gui/main_window.py b/src/cosmo/gui/main_window.py index a462d4e..8e99ddc 100644 --- a/src/cosmo/gui/main_window.py +++ b/src/cosmo/gui/main_window.py @@ -9,7 +9,7 @@ from pathlib import Path from typing import Optional -# Qt binding selection: PyQt5 → PySide6 → PyQt6 (same as your current GUI). [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_openlabel_to_omega.py) +# Qt binding selection: PyQt5 → PySide6 → PyQt6 (same as your current GUI). try: from PyQt5 import QtCore, QtGui, QtWidgets _QT_API = "PyQt5" diff --git a/src/cosmo/gui/workers.py b/src/cosmo/gui/workers.py index 16ad7a2..a884056 100644 --- a/src/cosmo/gui/workers.py +++ b/src/cosmo/gui/workers.py @@ -3,7 +3,7 @@ from typing import Callable -# Qt binding selection (same preference order as your current GUI) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_openlabel_to_omega.py) +# Qt binding selection (same preference order as your current GUI) try: from PyQt5 import QtCore _Signal = QtCore.pyqtSignal diff --git a/tests/cli_helpers.py b/tests/cli_helpers.py index 8576204..5d8643c 100644 --- a/tests/cli_helpers.py +++ b/tests/cli_helpers.py @@ -21,10 +21,10 @@ def run_cosmo(args: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str] env.setdefault("MPLBACKEND", "Agg") if os.name == "nt": - cmd = [sys.executable, "-m", "cosmo.cli.main", *args] # real entrypoint logic [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + cmd = [sys.executable, "-m", "cosmo.cli.main", *args] # real entrypoint logic else: exe = shutil.which("cosmo") - cmd = [exe, *args] if exe else [sys.executable, "-m", "cosmo.cli.main", *args] # same entrypoint [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + cmd = [exe, *args] if exe else [sys.executable, "-m", "cosmo.cli.main", *args] # same entrypoint return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, env=env) diff --git a/tests/conftest.py b/tests/conftest.py index 68388c3..4f94ab9 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -16,7 +16,7 @@ def run_cosmo(args: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str] Fallback: if the console script isn't on PATH (rare in CI), run via module entrypoint: python -m cosmo.cli.main - NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI. """ env = dict(os.environ) env.setdefault("MPLBACKEND", "Agg") # CI already sets this, but keep it deterministic. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) @@ -26,7 +26,7 @@ def run_cosmo(args: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str] cmd = [exe, *args] else: # Still the real entrypoint, just executed as a module. - cmd = [os.environ.get("PYTHON", "python"), "-m", "cosmo.cli.main", *args] # dispatch logic in main.py [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + cmd = [os.environ.get("PYTHON", "python"), "-m", "cosmo.cli.main", *args] # dispatch logic in main.py return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, env=env) diff --git a/tests/test_calibration_compute_whitebox.py b/tests/test_calibration_compute_whitebox.py index 0a54fc0..48f87c8 100644 --- a/tests/test_calibration_compute_whitebox.py +++ b/tests/test_calibration_compute_whitebox.py @@ -6,7 +6,7 @@ import numpy as np import pytest -from cosmo.calibration.compute import ( # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) +from cosmo.calibration.compute import ( # compute_calibration, write_calibration_outputs, ) @@ -75,7 +75,7 @@ def test_write_calibration_outputs_writes_expected_json_schema(tmp_path: Path): fps=30.0, image_width=3840, image_height=2160, - residuals_png_path=str(resid_png), # optional; may become None on plot errors [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) + residuals_png_path=str(resid_png), # optional; may become None on plot errors overlay_png_path=None, image_path=None, openlabel_path=None, @@ -85,7 +85,7 @@ def test_write_calibration_outputs_writes_expected_json_schema(tmp_path: Path): assert Path(outs.summary_json_path).is_file() calib = json.loads(Path(outs.calibration_json_path).read_text(encoding="utf-8")) - # Required keys written by write_calibration_outputs [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) + # Required keys written by write_calibration_outputs for k in ("fps", "image_width", "image_height", "homography", "intrinsics", "extrinsics", "default_dimensions_m"): assert k in calib H = calib["homography"] @@ -95,7 +95,7 @@ def test_write_calibration_outputs_writes_expected_json_schema(tmp_path: Path): for k in ("rmse_m", "inliers_count", "pairs_used", "pixel_points", "world_points_ENU_m", "homography"): assert k in summary - # Residual plot can be omitted if matplotlib errors; if present, ensure file exists [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) + # Residual plot can be omitted if matplotlib errors; if present, ensure file exists if outs.residuals_png_path is not None: assert Path(outs.residuals_png_path).is_file() diff --git a/tests/test_console_scripts_smoke.py b/tests/test_console_scripts_smoke.py index c2c94b9..6527104 100644 --- a/tests/test_console_scripts_smoke.py +++ b/tests/test_console_scripts_smoke.py @@ -50,19 +50,19 @@ def test_console_scripts_exist_and_help_works(): assert cosmo_calibrate, "Expected 'cosmo-calibrate' on PATH or in venv bin dir after install." assert cosmo_gui, "Expected 'cosmo-gui' on PATH or in venv bin dir after install." - # Always safe: module entrypoint tests real main.py behavior. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + # Always safe: module entrypoint tests real main.py behavior. res = _run([sys.executable, "-m", "cosmo.cli.main", "--version"]) assert res.returncode == 0, f"cosmo --version failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" assert res.stdout.strip(), "cosmo --version produced no output." - # Dispatch help via main.py (convert/calibrate are subcommands). [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + # Dispatch help via main.py (convert/calibrate are subcommands). res = _run([sys.executable, "-m", "cosmo.cli.main", "convert", "--help"]) assert res.returncode == 0, f"cosmo convert --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" res = _run([sys.executable, "-m", "cosmo.cli.main", "calibrate", "--help"]) assert res.returncode == 0, f"cosmo calibrate --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" - # Direct entrypoints: run via module on Windows to avoid blocked .EXE shims. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)[2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + # Direct entrypoints: run via module on Windows to avoid blocked .EXE shims. if os.name == "nt": res = _run([sys.executable, "-m", "cosmo.cli.convert", "--help"]) assert res.returncode == 0, f"python -m cosmo.cli.convert --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" @@ -76,4 +76,4 @@ def test_console_scripts_exist_and_help_works(): res = _run([cosmo_calibrate, "--help"]) assert res.returncode == 0, f"cosmo-calibrate --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" - # Do NOT execute cosmo-gui in tests: it launches GUI immediately and can fail headless. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)[3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + # Do NOT execute cosmo-gui in tests: it launches GUI immediately and can fail headless. diff --git a/tests/test_convert_app_e2e_not_smoke.py b/tests/test_convert_app_e2e_not_smoke.py index d5ce9e1..bfa902f 100644 --- a/tests/test_convert_app_e2e_not_smoke.py +++ b/tests/test_convert_app_e2e_not_smoke.py @@ -5,7 +5,7 @@ import json from pathlib import Path -from cosmo.app.convert_app import ( # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) +from cosmo.app.convert_app import ( # ConvertConfig, run_convert, ) diff --git a/tests/test_convert_no_scripts_fallback.py b/tests/test_convert_no_scripts_fallback.py index fa60d78..6e2d9e1 100644 --- a/tests/test_convert_no_scripts_fallback.py +++ b/tests/test_convert_no_scripts_fallback.py @@ -2,7 +2,7 @@ from __future__ import annotations from cosmo.app.convert_app import ( - _get_converter_or_raise, # internal helper [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) + _get_converter_or_raise, # internal helper ) @@ -11,7 +11,7 @@ def test_converter_import_has_clear_error_message(): fn = _get_converter_or_raise() assert callable(fn) except RuntimeError as e: - # If it fails, ensure the error message guides the developer correctly. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) + # If it fails, ensure the error message guides the developer correctly. msg = str(e) assert "COSMO converter implementation not found" in msg assert "cosmo.converters.openlabel_to_omega" in msg diff --git a/tests/test_core_calibrate_whitebox.py b/tests/test_core_calibrate_whitebox.py index 570a3d6..712c509 100644 --- a/tests/test_core_calibrate_whitebox.py +++ b/tests/test_core_calibrate_whitebox.py @@ -3,7 +3,7 @@ import json from pathlib import Path -from cosmo.app.calibrate_app import ( # app layer API [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) +from cosmo.app.calibrate_app import ( # app layer API CalibrateConfig, run_calibrate, ) @@ -34,7 +34,7 @@ def test_run_calibrate_writes_stem_based_outputs(tmp_path: Path): opendrive = tmp_path / "map.xodr" opendrive.write_text("", encoding="utf-8") - # Deterministic run folder: existing out_dir + run_name => out_dir/run_name [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) + # Deterministic run folder: existing out_dir + run_name => out_dir/run_name runs_dir = tmp_path / "runs" runs_dir.mkdir() @@ -55,7 +55,7 @@ def test_run_calibrate_writes_stem_based_outputs(tmp_path: Path): ) logs: list[str] = [] - result = run_calibrate(cfg, log_fn=logs.append) # same signature used by CLI [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) + result = run_calibrate(cfg, log_fn=logs.append) # same signature used by CLI run_dir = Path(result.run_dir) outputs_dir = Path(result.outputs_dir) @@ -63,7 +63,7 @@ def test_run_calibrate_writes_stem_based_outputs(tmp_path: Path): assert outputs_dir == run_dir / "outputs" assert outputs_dir.exists() - # Output naming is stem-based: _calibration.json etc. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) + # Output naming is stem-based: _calibration.json etc. calib_path = Path(result.calibration_json_path) assert calib_path.is_file() assert calib_path.name.endswith("_calibration.json") @@ -73,15 +73,15 @@ def test_run_calibrate_writes_stem_based_outputs(tmp_path: Path): H = calib["homography"] assert isinstance(H, list) and len(H) == 3 and all(len(r) == 3 for r in H) - # Summary JSON should be produced (unless compute/write changes) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) + # Summary JSON should be produced (unless compute/write changes) if result.summary_json_path: assert Path(result.summary_json_path).is_file() - # Residual plot is intended but can be None (matplotlib issues), so keep optional. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) + # Residual plot is intended but can be None (matplotlib issues), so keep optional. if result.residuals_png_path: assert Path(result.residuals_png_path).is_file() - # No overlay expected because image=None [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) + # No overlay expected because image=None assert result.overlay_png_path is None # Some logging should occur (useful diagnostic) diff --git a/tests/test_core_convert_whitebox.py b/tests/test_core_convert_whitebox.py index 9226017..c2fd688 100644 --- a/tests/test_core_convert_whitebox.py +++ b/tests/test_core_convert_whitebox.py @@ -4,7 +4,7 @@ import json from pathlib import Path -from cosmo.app.convert_app import ( # app layer API [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) +from cosmo.app.convert_app import ( # app layer API ConvertConfig, run_convert, ) @@ -31,7 +31,7 @@ def test_run_convert_writes_csv_and_sets_csv_path(tmp_path: Path): openlabel_path = tmp_path / "openlabel.json" openlabel_path.write_text(json.dumps(openlabel), encoding="utf-8") - # Minimal calibration file path (convert_app passes this to converter if provided) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) + # Minimal calibration file path (convert_app passes this to converter if provided) calib = { "fps": 30.0, "image_width": 3840, @@ -51,7 +51,7 @@ def test_run_convert_writes_csv_and_sets_csv_path(tmp_path: Path): calibration=str(calib_path), fps=None, write_csv=True, - write_mcap=False, # keep core tests independent of optional MCAP deps [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) + write_mcap=False, # keep core tests independent of optional MCAP deps swap_xy=False, flip_x=False, flip_y=False, @@ -62,7 +62,7 @@ def test_run_convert_writes_csv_and_sets_csv_path(tmp_path: Path): ) logs: list[str] = [] - result = run_convert(cfg, log_fn=logs.append) # same signature used by CLI [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) + result = run_convert(cfg, log_fn=logs.append) # same signature used by CLI run_dir = Path(result.run_dir) outputs_dir = Path(result.outputs_dir) @@ -70,7 +70,7 @@ def test_run_convert_writes_csv_and_sets_csv_path(tmp_path: Path): assert outputs_dir == run_dir / "outputs" assert outputs_dir.exists() - # csv_path is only set if the file exists (summary filters non-existent files) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) + # csv_path is only set if the file exists (summary filters non-existent files) assert result.csv_path is not None csv_path = Path(result.csv_path) assert csv_path.is_file() @@ -103,7 +103,7 @@ def test_run_convert_no_csv_results_in_none_csv_path(tmp_path: Path): write_mcap=False, out_dir=str(runs_dir), run_name="wb_no_csv", - ) # relies on defaults for other fields [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) + ) # relies on defaults for other fields result = run_convert(cfg, log_fn=lambda _: None) assert result.csv_path is None diff --git a/tests/test_integration_mcap_convert_app.py b/tests/test_integration_mcap_convert_app.py index 625494f..b181215 100644 --- a/tests/test_integration_mcap_convert_app.py +++ b/tests/test_integration_mcap_convert_app.py @@ -5,7 +5,7 @@ import pytest -from cosmo.app.convert_app import ( # [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) +from cosmo.app.convert_app import ( # ConvertConfig, run_convert, ) @@ -20,7 +20,7 @@ def _has_betterosi() -> bool: @pytest.mark.integration -@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) +@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # def test_run_convert_app_writes_mcap_and_sets_result_path(tmp_path: Path): # Minimal OpenLABEL openlabel = { @@ -36,7 +36,7 @@ def test_run_convert_app_writes_mcap_and_sets_result_path(tmp_path: Path): calib_path = tmp_path / "Calibration.json" calib_path.write_text(json.dumps({"fps": 10.0, "homography": [[1, 0, 0], [0, 1, 0], [0, 0, 1]]}), encoding="utf-8") - # OpenDRIVE embedded flag in convert_app summary is based on cfg.opendrive [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) + # OpenDRIVE embedded flag in convert_app summary is based on cfg.opendrive odr_path = tmp_path / "map.xodr" odr_path.write_text("", encoding="utf-8") @@ -51,14 +51,14 @@ def test_run_convert_app_writes_mcap_and_sets_result_path(tmp_path: Path): write_mcap=True, out_dir=str(runs_dir), run_name="int_mcap", - ) # config matches app layer [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) + ) # config matches app layer result = run_convert(cfg, log_fn=None) assert Path(result.run_dir).exists() assert Path(result.outputs_dir).exists() - # convert_app only sets mcap_path if file exists [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) + # convert_app only sets mcap_path if file exists assert result.mcap_path is not None assert Path(result.mcap_path).is_file() assert Path(result.mcap_path).stat().st_size > 0 diff --git a/tests/test_integration_mcap_converter.py b/tests/test_integration_mcap_converter.py index eab2a48..a770f68 100644 --- a/tests/test_integration_mcap_converter.py +++ b/tests/test_integration_mcap_converter.py @@ -6,7 +6,7 @@ import pytest from cosmo.converters.openlabel_to_omega import ( - convert_openlabel_to_omega, # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + convert_openlabel_to_omega, # ) @@ -37,7 +37,7 @@ def _try_read_mcap_topics(mcap_path: Path) -> set[str]: @pytest.mark.integration -@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) +@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # def test_converter_writes_mcap_with_ground_truth_and_map(tmp_path: Path): # Minimal OpenLABEL with two frames (ensures multiple GT messages) openlabel = { @@ -67,7 +67,7 @@ def test_converter_writes_mcap_with_ground_truth_and_map(tmp_path: Path): encoding="utf-8", ) - # Provide OpenDRIVE so map embedding path is exercised (ground_truth_map topic) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + # Provide OpenDRIVE so map embedding path is exercised (ground_truth_map topic) odr_path = tmp_path / "map.xodr" odr_path.write_text("", encoding="utf-8") @@ -82,7 +82,7 @@ def test_converter_writes_mcap_with_ground_truth_and_map(tmp_path: Path): georef_data_path=None, fps_arg=None, write_csv=False, # focus on MCAP in this integration test - write_mcap=True, # requires betterosi [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + write_mcap=True, # requires betterosi swap_xy=False, flip_x=False, flip_y=False, @@ -98,6 +98,6 @@ def test_converter_writes_mcap_with_ground_truth_and_map(tmp_path: Path): # Optional deeper validation if the `mcap` reader is installed: topics = _try_read_mcap_topics(mcap_path) if topics: - # Converter writes these topics without leading '/' [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + # Converter writes these topics without leading '/' assert "ground_truth" in topics assert "ground_truth_map" in topics diff --git a/tests/test_integration_mcap_gt_count.py b/tests/test_integration_mcap_gt_count.py index 7d7d2e4..fffca3c 100644 --- a/tests/test_integration_mcap_gt_count.py +++ b/tests/test_integration_mcap_gt_count.py @@ -23,7 +23,7 @@ import pytest -from cosmo.app.convert_app import ( # app layer [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) +from cosmo.app.convert_app import ( # app layer ConvertConfig, run_convert, ) @@ -62,7 +62,7 @@ def _count_topic_messages(mcap_path: Path, topic: str) -> int: @pytest.mark.integration -@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; cannot write MCAP") # converter depends on it [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) +@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; cannot write MCAP") # converter depends on it @pytest.mark.skipif(not has_mcap_reader(), reason="mcap reader not installed; cannot inspect MCAP") def test_mcap_gt_count_map_once_and_strictly_increasing_log_time(tmp_path: Path): # Create OpenLABEL with a known number of frames. @@ -104,7 +104,7 @@ def test_mcap_gt_count_map_once_and_strictly_increasing_log_time(tmp_path: Path) encoding="utf-8", ) - # OpenDRIVE included so map is embedded as `ground_truth_map` (written once with log_time=0) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + # OpenDRIVE included so map is embedded as `ground_truth_map` (written once with log_time=0) odr_path = tmp_path / "map.xodr" odr_path.write_text("", encoding="utf-8") @@ -119,10 +119,10 @@ def test_mcap_gt_count_map_once_and_strictly_increasing_log_time(tmp_path: Path) write_mcap=True, out_dir=str(runs_dir), run_name="mcap_gt_count", - ) # app layer writes into per-run outputs/ folder [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) + ) # app layer writes into per-run outputs/ folder result = run_convert(cfg, log_fn=None) - assert result.mcap_path, "Expected mcap_path to be set when MCAP is written" # app layer sets only if file exists [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) + assert result.mcap_path, "Expected mcap_path to be set when MCAP is written" # app layer sets only if file exists mcap_path = assert_mcap_nonempty(result.mcap_path) @@ -130,11 +130,11 @@ def test_mcap_gt_count_map_once_and_strictly_increasing_log_time(tmp_path: Path) gt_times = _collect_topic_log_times(mcap_path, "ground_truth") assert len(gt_times) == len(frame_ids), f"Expected {len(frame_ids)} ground_truth messages, got {len(gt_times)}" - # 2) Map message written exactly once at log_time=0 (converter writes once) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + # 2) Map message written exactly once at log_time=0 (converter writes once) map_times = _collect_topic_log_times(mcap_path, "ground_truth_map") assert len(map_times) == 1, f"Expected exactly 1 ground_truth_map message, got {len(map_times)}" assert map_times[0] == 0, f"Expected ground_truth_map log_time=0, got {map_times[0]}" - # 3) GroundTruth log_time strictly increasing (converter uses log_time=total_nanos) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + # 3) GroundTruth log_time strictly increasing (converter uses log_time=total_nanos) assert gt_times == sorted(gt_times), f"ground_truth log_time not non-decreasing: {gt_times}" assert all(b > a for a, b in zip(gt_times, gt_times[1:])), f"ground_truth log_time not strictly increasing: {gt_times}" diff --git a/tests/test_main_dispatch.py b/tests/test_main_dispatch.py index c784b48..8dc4554 100644 --- a/tests/test_main_dispatch.py +++ b/tests/test_main_dispatch.py @@ -14,5 +14,5 @@ def test_main_version_works_without_gui(): text=True, env={**os.environ, "MPLBACKEND": "Agg"}, ) - assert res.returncode == 0 # main.py returns 0 on --version [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) - assert res.stdout.strip() # prints version string [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + assert res.returncode == 0 # main.py returns 0 on --version + assert res.stdout.strip() # prints version string diff --git a/tests/test_openlabel_to_omega_whitebox.py b/tests/test_openlabel_to_omega_whitebox.py index 22f267f..1fedbd5 100644 --- a/tests/test_openlabel_to_omega_whitebox.py +++ b/tests/test_openlabel_to_omega_whitebox.py @@ -6,7 +6,7 @@ import numpy as np -from cosmo.converters.openlabel_to_omega import ( # [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) +from cosmo.converters.openlabel_to_omega import ( # convert_openlabel_to_omega, load_alignment, parse_openlabel, @@ -82,7 +82,7 @@ def test_convert_openlabel_to_omega_writes_csv_only(tmp_path: Path): georef_data_path=None, fps_arg=None, write_csv=True, - write_mcap=False, # ensure no betterosi dependency [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + write_mcap=False, # ensure no betterosi dependency swap_xy=False, flip_x=False, flip_y=False, @@ -96,11 +96,11 @@ def test_convert_openlabel_to_omega_writes_csv_only(tmp_path: Path): with csv_path.open(newline="", encoding="utf-8") as f: reader = csv.DictReader(f) - # Columns defined in module [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + # Columns defined in module for col in ("total_nanos", "idx", "x", "y", "yaw", "type_name", "subtype_name", "role_name"): assert col in (reader.fieldnames or []) rows = list(reader) assert len(rows) >= 2 - # Ensure time is non-decreasing (rows are sorted by total_nanos) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) + # Ensure time is non-decreasing (rows are sorted by total_nanos) times = [int(r["total_nanos"]) for r in rows] assert times == sorted(times) From 64676838659a34e759897cf24454394d435f237a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20Thors=C3=A9n?= <10030664+MickOls@users.noreply.github.com> Date: Wed, 24 Jun 2026 15:20:25 +0200 Subject: [PATCH 2/2] Remove remaining internal references and patch artifacts --- .github/workflows/ci.yml | 4 +- README.md | 2 +- cosmo_required_fixes.patch | 533 --------------------- cosmo_required_fixes.patch:Zone.Identifier | Bin 25 -> 0 bytes docs/how-to/calibration.md | 4 +- docs/reference/inputs-openlabel.md | 2 +- docs/reference/outputs-omega-prime.md | 2 +- docs/user_guide/cli.md | 6 +- tests/conftest.py | 2 +- tests/test_calibrate_cli_validation.py | 2 +- tests/test_cli_contracts_not_smoke.py | 4 +- tests/test_cli_smoke_calibrate.py | 6 +- tests/test_cli_smoke_convert.py | 4 +- tests/test_convert_alignment_flags.py | 2 +- tests/test_convert_cli_validation.py | 2 +- tests/test_convert_outputs.py | 2 +- 16 files changed, 22 insertions(+), 555 deletions(-) delete mode 100644 cosmo_required_fixes.patch delete mode 100644 cosmo_required_fixes.patch:Zone.Identifier diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 831b249..592c0f3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,7 +36,7 @@ jobs: run: ruff check . # Optional but recommended: verifies console scripts + dispatch without launching GUI. - # NOTE: calling plain `cosmo` (no args) attempts to start the GUI by default. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) + # NOTE: calling plain `cosmo` (no args) attempts to start the GUI by default. - name: CLI sanity check (no GUI) run: | cosmo --version @@ -78,4 +78,4 @@ jobs: - name: Run integration tests env: MPLBACKEND: Agg - run: pytest -q -m integration \ No newline at end of file + run: pytest -q -m integration diff --git a/README.md b/README.md index 91b1d4b..4513c13 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Calibration outputs are written to outputs/ as: - _calibration.json - _homography_fit_summary.json - _homography_fit_residuals.png -- _overlay_markers_on_image.png (only if --image is provided) +- _overlay_markers_on_image.png (only if --image is provided) Use the calibration file for conversion fallback: diff --git a/cosmo_required_fixes.patch b/cosmo_required_fixes.patch deleted file mode 100644 index d118fa4..0000000 --- a/cosmo_required_fixes.patch +++ /dev/null @@ -1,533 +0,0 @@ ---- a/.github/workflows/ci.yml -+++ b/.github/workflows/ci.yml -@@ -3,6 +3,10 @@ - on: - push: - pull_request: -+ workflow_dispatch: -+ schedule: -+ # Run optional/heavier integration tests weekly and on demand. -+ - cron: "0 3 * * 1" - - jobs: - test: ---- a/README.md -+++ b/README.md -@@ -52,8 +52,8 @@ - > tools explicitly: `pip install -e . pytest ruff` (adding `betterosi mcap` for - > MCAP integration tests). - --> Tip 1: The **GUI** is launced with `cosmo` or `cosmo gui`. --> Tip 2: During development with downloaded repro, all `cosmo` commands can be replaced with `python run_cosmo.py ...`, `cosmo.cmd ...` or `cosmo.ps1 ...`. `python run_gui.py` always starts the **GUI**. -+> Tip 1: The **GUI** is launched with `cosmo` or `cosmo gui`. -+> Tip 2: In headless environments, prefer explicit subcommands such as `cosmo convert ...` rather than plain `cosmo`, which may start the GUI. - - Convert using [ORBIT](https://github.com/RI-SE/ORBIT) georef as the primary pixel→ground mapping (**Convert** tab in the **GUI**): - ---- a/docs/getting-started/quickstart.md -+++ b/docs/getting-started/quickstart.md -@@ -10,17 +10,16 @@ - cosmo gui - ``` - --Development shortcut (either of): -+Use the installed console script: - - ```bash --python run_gui.py # Start GUI --python run_cosmo.py gui # Same behaviour as cosmo (when installed) --cosmo.cmd gui # Same behaviour in Windows cmd termial as cosmo (when installed) --cosmo.ps1 gui # Same behaviour in Windows PowerShell termial as cosmo (when installed) -+cosmo gui -+# or simply: -+cosmo - ``` - --- - ## CLI --> During development `cosmo ...` can be replaced with `python run_cosmo ...`, `cosmo.exe ...` or `cosmo.ps1 ...` without needing to install cosmo, e.g., `python run_cosmo.py convert ...`. To install for development use `uv sync --group dev` or `pip install -e . --group dev`. -+> For development, install the package first (for example `uv sync --group dev` or `pip install -e . --group dev`) and then use the `cosmo ...` console script. - --- - ### Convert (recommended: ORBIT georef) - ---- a/src/cosmo/converters/openlabel_to_omega.py -+++ b/src/cosmo/converters/openlabel_to_omega.py -@@ -721,7 +721,7 @@ - # ---------------------------- - # IMPORTANT: - # Use betterosi.Writer as a context manager to ensure MCAP footer/summary are finalized properly. [1](https://deepwiki.com/ika-rwth-aachen/omega-prime/3.1-loading-and-saving-recordings)[4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/) -- # Also write topic names without leading "/" to match common omega-prime usage. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py)[2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ # Also write topic names without leading "/" to match common omega-prime usage. - - def _write_map(writer_mcap): - if odr_path and os.path.isfile(odr_path): -@@ -752,7 +752,7 @@ - gt.proj_string = _gt_proj_string - if _gt_country_code: - gt.country_code = _gt_country_code -- # Provide log_time explicitly; readers often build indices from it. [4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/)[3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+ # Provide log_time explicitly; readers often build indices from it. [4](https://ika-rwth-aachen.github.io/omega-prime/notebooks/tutorial/) - writer_mcap.add(gt, topic="ground_truth", log_time=total_nanos) - - # If MCAP enabled, write with context manager; else only build CSV rows ---- a/src/cosmo/gui/main_window.py -+++ b/src/cosmo/gui/main_window.py -@@ -9,7 +9,7 @@ - from pathlib import Path - from typing import Optional - --# Qt binding selection: PyQt5 → PySide6 → PyQt6 (same as your current GUI). [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_openlabel_to_omega.py) -+# Qt binding selection: PyQt5 → PySide6 → PyQt6 (same as your current GUI). - try: - from PyQt5 import QtCore, QtGui, QtWidgets - _QT_API = "PyQt5" ---- a/src/cosmo/gui/workers.py -+++ b/src/cosmo/gui/workers.py -@@ -3,7 +3,7 @@ - - from typing import Callable - --# Qt binding selection (same preference order as your current GUI) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_openlabel_to_omega.py) -+# Qt binding selection (same preference order as your current GUI) - try: - from PyQt5 import QtCore - _Signal = QtCore.pyqtSignal ---- a/tests/cli_helpers.py -+++ b/tests/cli_helpers.py -@@ -21,10 +21,10 @@ - env.setdefault("MPLBACKEND", "Agg") - - if os.name == "nt": -- cmd = [sys.executable, "-m", "cosmo.cli.main", *args] # real entrypoint logic [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ cmd = [sys.executable, "-m", "cosmo.cli.main", *args] # real entrypoint logic - else: - exe = shutil.which("cosmo") -- cmd = [exe, *args] if exe else [sys.executable, "-m", "cosmo.cli.main", *args] # same entrypoint [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ cmd = [exe, *args] if exe else [sys.executable, "-m", "cosmo.cli.main", *args] # same entrypoint - - return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, env=env) - ---- a/tests/conftest.py -+++ b/tests/conftest.py -@@ -16,7 +16,7 @@ - Fallback: if the console script isn't on PATH (rare in CI), run via module entrypoint: - python -m cosmo.cli.main - -- NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI. - """ - env = dict(os.environ) - env.setdefault("MPLBACKEND", "Agg") # CI already sets this, but keep it deterministic. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) -@@ -26,7 +26,7 @@ - cmd = [exe, *args] - else: - # Still the real entrypoint, just executed as a module. -- cmd = [os.environ.get("PYTHON", "python"), "-m", "cosmo.cli.main", *args] # dispatch logic in main.py [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ cmd = [os.environ.get("PYTHON", "python"), "-m", "cosmo.cli.main", *args] # dispatch logic in main.py - - return subprocess.run(cmd, cwd=str(cwd), capture_output=True, text=True, env=env) - ---- a/tests/test_calibration_compute_whitebox.py -+++ b/tests/test_calibration_compute_whitebox.py -@@ -6,7 +6,7 @@ - import numpy as np - import pytest - --from cosmo.calibration.compute import ( # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+from cosmo.calibration.compute import ( # - compute_calibration, - write_calibration_outputs, - ) -@@ -75,7 +75,7 @@ - fps=30.0, - image_width=3840, - image_height=2160, -- residuals_png_path=str(resid_png), # optional; may become None on plot errors [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+ residuals_png_path=str(resid_png), # optional; may become None on plot errors - overlay_png_path=None, - image_path=None, - openlabel_path=None, -@@ -85,7 +85,7 @@ - assert Path(outs.summary_json_path).is_file() - - calib = json.loads(Path(outs.calibration_json_path).read_text(encoding="utf-8")) -- # Required keys written by write_calibration_outputs [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+ # Required keys written by write_calibration_outputs - for k in ("fps", "image_width", "image_height", "homography", "intrinsics", "extrinsics", "default_dimensions_m"): - assert k in calib - H = calib["homography"] -@@ -95,7 +95,7 @@ - for k in ("rmse_m", "inliers_count", "pairs_used", "pixel_points", "world_points_ENU_m", "homography"): - assert k in summary - -- # Residual plot can be omitted if matplotlib errors; if present, ensure file exists [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+ # Residual plot can be omitted if matplotlib errors; if present, ensure file exists - if outs.residuals_png_path is not None: - assert Path(outs.residuals_png_path).is_file() - ---- a/tests/test_console_scripts_smoke.py -+++ b/tests/test_console_scripts_smoke.py -@@ -50,19 +50,19 @@ - assert cosmo_calibrate, "Expected 'cosmo-calibrate' on PATH or in venv bin dir after install." - assert cosmo_gui, "Expected 'cosmo-gui' on PATH or in venv bin dir after install." - -- # Always safe: module entrypoint tests real main.py behavior. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ # Always safe: module entrypoint tests real main.py behavior. - res = _run([sys.executable, "-m", "cosmo.cli.main", "--version"]) - assert res.returncode == 0, f"cosmo --version failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" - assert res.stdout.strip(), "cosmo --version produced no output." - -- # Dispatch help via main.py (convert/calibrate are subcommands). [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ # Dispatch help via main.py (convert/calibrate are subcommands). - res = _run([sys.executable, "-m", "cosmo.cli.main", "convert", "--help"]) - assert res.returncode == 0, f"cosmo convert --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" - - res = _run([sys.executable, "-m", "cosmo.cli.main", "calibrate", "--help"]) - assert res.returncode == 0, f"cosmo calibrate --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" - -- # Direct entrypoints: run via module on Windows to avoid blocked .EXE shims. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)[2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ # Direct entrypoints: run via module on Windows to avoid blocked .EXE shims. - if os.name == "nt": - res = _run([sys.executable, "-m", "cosmo.cli.convert", "--help"]) - assert res.returncode == 0, f"python -m cosmo.cli.convert --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" -@@ -76,4 +76,4 @@ - res = _run([cosmo_calibrate, "--help"]) - assert res.returncode == 0, f"cosmo-calibrate --help failed.\nSTDOUT:\n{res.stdout}\nSTDERR:\n{res.stderr}" - -- # Do NOT execute cosmo-gui in tests: it launches GUI immediately and can fail headless. [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py)[3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ # Do NOT execute cosmo-gui in tests: it launches GUI immediately and can fail headless. ---- a/tests/test_convert_app_e2e_not_smoke.py -+++ b/tests/test_convert_app_e2e_not_smoke.py -@@ -5,7 +5,7 @@ - import json - from pathlib import Path - --from cosmo.app.convert_app import ( # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+from cosmo.app.convert_app import ( # - ConvertConfig, - run_convert, - ) ---- a/tests/test_convert_no_scripts_fallback.py -+++ b/tests/test_convert_no_scripts_fallback.py -@@ -2,7 +2,7 @@ - from __future__ import annotations - - from cosmo.app.convert_app import ( -- _get_converter_or_raise, # internal helper [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+ _get_converter_or_raise, # internal helper - ) - - -@@ -11,7 +11,7 @@ - fn = _get_converter_or_raise() - assert callable(fn) - except RuntimeError as e: -- # If it fails, ensure the error message guides the developer correctly. [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+ # If it fails, ensure the error message guides the developer correctly. - msg = str(e) - assert "COSMO converter implementation not found" in msg - assert "cosmo.converters.openlabel_to_omega" in msg ---- a/tests/test_core_calibrate_whitebox.py -+++ b/tests/test_core_calibrate_whitebox.py -@@ -3,7 +3,7 @@ - import json - from pathlib import Path - --from cosmo.app.calibrate_app import ( # app layer API [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) -+from cosmo.app.calibrate_app import ( # app layer API - CalibrateConfig, - run_calibrate, - ) -@@ -34,7 +34,7 @@ - opendrive = tmp_path / "map.xodr" - opendrive.write_text("", encoding="utf-8") - -- # Deterministic run folder: existing out_dir + run_name => out_dir/run_name [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) -+ # Deterministic run folder: existing out_dir + run_name => out_dir/run_name - runs_dir = tmp_path / "runs" - runs_dir.mkdir() - -@@ -55,7 +55,7 @@ - ) - - logs: list[str] = [] -- result = run_calibrate(cfg, log_fn=logs.append) # same signature used by CLI [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) -+ result = run_calibrate(cfg, log_fn=logs.append) # same signature used by CLI - - run_dir = Path(result.run_dir) - outputs_dir = Path(result.outputs_dir) -@@ -63,7 +63,7 @@ - assert outputs_dir == run_dir / "outputs" - assert outputs_dir.exists() - -- # Output naming is stem-based: _calibration.json etc. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) -+ # Output naming is stem-based: _calibration.json etc. - calib_path = Path(result.calibration_json_path) - assert calib_path.is_file() - assert calib_path.name.endswith("_calibration.json") -@@ -73,15 +73,15 @@ - H = calib["homography"] - assert isinstance(H, list) and len(H) == 3 and all(len(r) == 3 for r in H) - -- # Summary JSON should be produced (unless compute/write changes) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) -+ # Summary JSON should be produced (unless compute/write changes) - if result.summary_json_path: - assert Path(result.summary_json_path).is_file() - -- # Residual plot is intended but can be None (matplotlib issues), so keep optional. [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) -+ # Residual plot is intended but can be None (matplotlib issues), so keep optional. - if result.residuals_png_path: - assert Path(result.residuals_png_path).is_file() - -- # No overlay expected because image=None [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/calibrate_app.py) -+ # No overlay expected because image=None - assert result.overlay_png_path is None - - # Some logging should occur (useful diagnostic) ---- a/tests/test_core_convert_whitebox.py -+++ b/tests/test_core_convert_whitebox.py -@@ -4,7 +4,7 @@ - import json - from pathlib import Path - --from cosmo.app.convert_app import ( # app layer API [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+from cosmo.app.convert_app import ( # app layer API - ConvertConfig, - run_convert, - ) -@@ -31,7 +31,7 @@ - openlabel_path = tmp_path / "openlabel.json" - openlabel_path.write_text(json.dumps(openlabel), encoding="utf-8") - -- # Minimal calibration file path (convert_app passes this to converter if provided) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+ # Minimal calibration file path (convert_app passes this to converter if provided) - calib = { - "fps": 30.0, - "image_width": 3840, -@@ -51,7 +51,7 @@ - calibration=str(calib_path), - fps=None, - write_csv=True, -- write_mcap=False, # keep core tests independent of optional MCAP deps [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+ write_mcap=False, # keep core tests independent of optional MCAP deps - swap_xy=False, - flip_x=False, - flip_y=False, -@@ -62,7 +62,7 @@ - ) - - logs: list[str] = [] -- result = run_convert(cfg, log_fn=logs.append) # same signature used by CLI [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+ result = run_convert(cfg, log_fn=logs.append) # same signature used by CLI - - run_dir = Path(result.run_dir) - outputs_dir = Path(result.outputs_dir) -@@ -70,7 +70,7 @@ - assert outputs_dir == run_dir / "outputs" - assert outputs_dir.exists() - -- # csv_path is only set if the file exists (summary filters non-existent files) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+ # csv_path is only set if the file exists (summary filters non-existent files) - assert result.csv_path is not None - csv_path = Path(result.csv_path) - assert csv_path.is_file() -@@ -103,7 +103,7 @@ - write_mcap=False, - out_dir=str(runs_dir), - run_name="wb_no_csv", -- ) # relies on defaults for other fields [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/convert_app.py) -+ ) # relies on defaults for other fields - - result = run_convert(cfg, log_fn=lambda _: None) - assert result.csv_path is None ---- a/tests/test_integration_mcap_convert_app.py -+++ b/tests/test_integration_mcap_convert_app.py -@@ -5,7 +5,7 @@ - - import pytest - --from cosmo.app.convert_app import ( # [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+from cosmo.app.convert_app import ( # - ConvertConfig, - run_convert, - ) -@@ -20,7 +20,7 @@ - - - @pytest.mark.integration --@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # - def test_run_convert_app_writes_mcap_and_sets_result_path(tmp_path: Path): - # Minimal OpenLABEL - openlabel = { -@@ -36,7 +36,7 @@ - calib_path = tmp_path / "Calibration.json" - calib_path.write_text(json.dumps({"fps": 10.0, "homography": [[1, 0, 0], [0, 1, 0], [0, 0, 1]]}), encoding="utf-8") - -- # OpenDRIVE embedded flag in convert_app summary is based on cfg.opendrive [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+ # OpenDRIVE embedded flag in convert_app summary is based on cfg.opendrive - odr_path = tmp_path / "map.xodr" - odr_path.write_text("", encoding="utf-8") - -@@ -51,14 +51,14 @@ - write_mcap=True, - out_dir=str(runs_dir), - run_name="int_mcap", -- ) # config matches app layer [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+ ) # config matches app layer - - result = run_convert(cfg, log_fn=None) - - assert Path(result.run_dir).exists() - assert Path(result.outputs_dir).exists() - -- # convert_app only sets mcap_path if file exists [3](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+ # convert_app only sets mcap_path if file exists - assert result.mcap_path is not None - assert Path(result.mcap_path).is_file() - assert Path(result.mcap_path).stat().st_size > 0 ---- a/tests/test_integration_mcap_converter.py -+++ b/tests/test_integration_mcap_converter.py -@@ -6,7 +6,7 @@ - import pytest - - from cosmo.converters.openlabel_to_omega import ( -- convert_openlabel_to_omega, # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ convert_openlabel_to_omega, # - ) - - -@@ -37,7 +37,7 @@ - - - @pytest.mark.integration --@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; MCAP integration test skipped") # - def test_converter_writes_mcap_with_ground_truth_and_map(tmp_path: Path): - # Minimal OpenLABEL with two frames (ensures multiple GT messages) - openlabel = { -@@ -67,7 +67,7 @@ - encoding="utf-8", - ) - -- # Provide OpenDRIVE so map embedding path is exercised (ground_truth_map topic) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ # Provide OpenDRIVE so map embedding path is exercised (ground_truth_map topic) - odr_path = tmp_path / "map.xodr" - odr_path.write_text("", encoding="utf-8") - -@@ -82,7 +82,7 @@ - georef_data_path=None, - fps_arg=None, - write_csv=False, # focus on MCAP in this integration test -- write_mcap=True, # requires betterosi [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ write_mcap=True, # requires betterosi - swap_xy=False, - flip_x=False, - flip_y=False, -@@ -98,6 +98,6 @@ - # Optional deeper validation if the `mcap` reader is installed: - topics = _try_read_mcap_topics(mcap_path) - if topics: -- # Converter writes these topics without leading '/' [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ # Converter writes these topics without leading '/' - assert "ground_truth" in topics - assert "ground_truth_map" in topics ---- a/tests/test_integration_mcap_gt_count.py -+++ b/tests/test_integration_mcap_gt_count.py -@@ -23,7 +23,7 @@ - - import pytest - --from cosmo.app.convert_app import ( # app layer [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+from cosmo.app.convert_app import ( # app layer - ConvertConfig, - run_convert, - ) -@@ -62,7 +62,7 @@ - - - @pytest.mark.integration --@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; cannot write MCAP") # converter depends on it [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+@pytest.mark.skipif(not _has_betterosi(), reason="betterosi not installed; cannot write MCAP") # converter depends on it - @pytest.mark.skipif(not has_mcap_reader(), reason="mcap reader not installed; cannot inspect MCAP") - def test_mcap_gt_count_map_once_and_strictly_increasing_log_time(tmp_path: Path): - # Create OpenLABEL with a known number of frames. -@@ -104,7 +104,7 @@ - encoding="utf-8", - ) - -- # OpenDRIVE included so map is embedded as `ground_truth_map` (written once with log_time=0) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ # OpenDRIVE included so map is embedded as `ground_truth_map` (written once with log_time=0) - odr_path = tmp_path / "map.xodr" - odr_path.write_text("", encoding="utf-8") - -@@ -119,10 +119,10 @@ - write_mcap=True, - out_dir=str(runs_dir), - run_name="mcap_gt_count", -- ) # app layer writes into per-run outputs/ folder [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+ ) # app layer writes into per-run outputs/ folder - - result = run_convert(cfg, log_fn=None) -- assert result.mcap_path, "Expected mcap_path to be set when MCAP is written" # app layer sets only if file exists [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/compute.py) -+ assert result.mcap_path, "Expected mcap_path to be set when MCAP is written" # app layer sets only if file exists - - mcap_path = assert_mcap_nonempty(result.mcap_path) - -@@ -130,11 +130,11 @@ - gt_times = _collect_topic_log_times(mcap_path, "ground_truth") - assert len(gt_times) == len(frame_ids), f"Expected {len(frame_ids)} ground_truth messages, got {len(gt_times)}" - -- # 2) Map message written exactly once at log_time=0 (converter writes once) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ # 2) Map message written exactly once at log_time=0 (converter writes once) - map_times = _collect_topic_log_times(mcap_path, "ground_truth_map") - assert len(map_times) == 1, f"Expected exactly 1 ground_truth_map message, got {len(map_times)}" - assert map_times[0] == 0, f"Expected ground_truth_map log_time=0, got {map_times[0]}" - -- # 3) GroundTruth log_time strictly increasing (converter uses log_time=total_nanos) [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ # 3) GroundTruth log_time strictly increasing (converter uses log_time=total_nanos) - assert gt_times == sorted(gt_times), f"ground_truth log_time not non-decreasing: {gt_times}" - assert all(b > a for a, b in zip(gt_times, gt_times[1:])), f"ground_truth log_time not strictly increasing: {gt_times}" ---- a/tests/test_main_dispatch.py -+++ b/tests/test_main_dispatch.py -@@ -14,5 +14,5 @@ - text=True, - env={**os.environ, "MPLBACKEND": "Agg"}, - ) -- assert res.returncode == 0 # main.py returns 0 on --version [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -- assert res.stdout.strip() # prints version string [1](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/main.py) -+ assert res.returncode == 0 # main.py returns 0 on --version -+ assert res.stdout.strip() # prints version string ---- a/tests/test_openlabel_to_omega_whitebox.py -+++ b/tests/test_openlabel_to_omega_whitebox.py -@@ -6,7 +6,7 @@ - - import numpy as np - --from cosmo.converters.openlabel_to_omega import ( # [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+from cosmo.converters.openlabel_to_omega import ( # - convert_openlabel_to_omega, - load_alignment, - parse_openlabel, -@@ -82,7 +82,7 @@ - georef_data_path=None, - fps_arg=None, - write_csv=True, -- write_mcap=False, # ensure no betterosi dependency [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ write_mcap=False, # ensure no betterosi dependency - swap_xy=False, - flip_x=False, - flip_y=False, -@@ -96,11 +96,11 @@ - - with csv_path.open(newline="", encoding="utf-8") as f: - reader = csv.DictReader(f) -- # Columns defined in module [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ # Columns defined in module - for col in ("total_nanos", "idx", "x", "y", "yaw", "type_name", "subtype_name", "role_name"): - assert col in (reader.fieldnames or []) - rows = list(reader) - assert len(rows) >= 2 -- # Ensure time is non-decreasing (rows are sorted by total_nanos) [2](https://risecloud-my.sharepoint.com/personal/anders_thorsen_ri_se/Documents/Microsoft%20Copilot%20Chat%20Files/openlabel_to_omega.py) -+ # Ensure time is non-decreasing (rows are sorted by total_nanos) - times = [int(r["total_nanos"]) for r in rows] - assert times == sorted(times) diff --git a/cosmo_required_fixes.patch:Zone.Identifier b/cosmo_required_fixes.patch:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2x_homography_fit_summary.json - _homography_fit_residuals.png -- _overlay_markers_on_image.png (only if --image was supplied) +- _overlay_markers_on_image.png (only if --image was supplied) ## Step 3 — Convert using calibration @@ -31,4 +31,4 @@ cosmo convert scenario.json \ If results look wrong, check the calibration run’s run_summary.json for: - RMSE and inlier count -- notes about missing plots/overlays +- notes about missing plots/overlays diff --git a/docs/reference/inputs-openlabel.md b/docs/reference/inputs-openlabel.md index ac7e38f..e776e92 100644 --- a/docs/reference/inputs-openlabel.md +++ b/docs/reference/inputs-openlabel.md @@ -1,6 +1,6 @@ # Input: OpenLABEL (expected structure) -COSMO reads OpenLABEL JSON and extracts object metadata and per-frame rotated bounding boxes (rbbox). +COSMO reads OpenLABEL JSON and extracts object metadata and per-frame rotated bounding boxes (rbbox). ## Required content COSMO expects, per frame, objects that contain an `rbbox` with at least five values: diff --git a/docs/reference/outputs-omega-prime.md b/docs/reference/outputs-omega-prime.md index f4fefdb..a83041a 100644 --- a/docs/reference/outputs-omega-prime.md +++ b/docs/reference/outputs-omega-prime.md @@ -1,6 +1,6 @@ # Outputs: Omega‑Prime CSV + OSI/MCAP -COSMO writes all outputs inside a per-run folder under `runs/` by default. +COSMO writes all outputs inside a per-run folder under `runs/` by default. --- diff --git a/docs/user_guide/cli.md b/docs/user_guide/cli.md index 890e2f1..22e6a9d 100644 --- a/docs/user_guide/cli.md +++ b/docs/user_guide/cli.md @@ -61,7 +61,7 @@ cosmo convert scenario.json \ --xy-offset 1.2 -0.5 \ -o runs/ ``` -Available flags: `--swap-xy`, `--flip-x`, `--flip-y`, `--xy-offset DX DY`, `--yaw-offset-deg DEG`. +Available flags: `--swap-xy`, `--flip-x`, `--flip-y`, `--xy-offset DX DY`, `--yaw-offset-deg DEG`. ### Run naming & JSON output ```bash cosmo convert scenario.json --run-name saro_roundabout -o runs/ @@ -144,7 +144,7 @@ cosmo calibrate --inputs pixel_pairs.csv visual_markers.csv map.xodr \ ##### Useful knobs - --ransac-thresh-m (default 0.50) — RANSAC distance threshold in meters --fps, -- --image-width, --image-height +- --image-width, --image-height ##### Calibration outputs Calibration writes stem-based files under outputs/: @@ -152,5 +152,5 @@ Calibration writes stem-based files under outputs/: - _calibration.json - _homography_fit_summary.json - _homography_fit_residuals.png -- _overlay_markers_on_image.png (only if --image is provided) +- _overlay_markers_on_image.png (only if --image is provided) diff --git a/tests/conftest.py b/tests/conftest.py index 4f94ab9..3f80e69 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -19,7 +19,7 @@ def run_cosmo(args: list[str], *, cwd: Path) -> subprocess.CompletedProcess[str] NOTE: Never call plain `cosmo` with no args in CI, because cosmo.cli.main defaults to GUI. """ env = dict(os.environ) - env.setdefault("MPLBACKEND", "Agg") # CI already sets this, but keep it deterministic. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) + env.setdefault("MPLBACKEND", "Agg") # CI already sets this, but keep it deterministic. exe = shutil.which("cosmo") if exe: diff --git a/tests/test_calibrate_cli_validation.py b/tests/test_calibrate_cli_validation.py index 0740b11..9eefbb6 100644 --- a/tests/test_calibrate_cli_validation.py +++ b/tests/test_calibrate_cli_validation.py @@ -20,4 +20,4 @@ def test_calibrate_rejects_mixed_input_styles(tmp_path: Path): cwd=tmp_path, ) assert res.returncode != 0 - assert "Mixed input styles detected" in (res.stderr + res.stdout) # error text comes from argparse error [2](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd) + assert "Mixed input styles detected" in (res.stderr + res.stdout) # error text comes from argparse error diff --git a/tests/test_cli_contracts_not_smoke.py b/tests/test_cli_contracts_not_smoke.py index 6df2b00..1c25858 100644 --- a/tests/test_cli_contracts_not_smoke.py +++ b/tests/test_cli_contracts_not_smoke.py @@ -14,7 +14,7 @@ def test_calibrate_rejects_mixed_input_styles(tmp_path: Path): vm.write_text("point_name,E,N\np1,0,0\np2,1,0\np3,0,1\np4,1,1\n", encoding="utf-8") odr.write_text("", encoding="utf-8") - # Mix two styles intentionally -> should error (logic is in calibrate.py) [2](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd) + # Mix two styles intentionally -> should error (logic is in calibrate.py) res = run_cosmo( ["calibrate", "--inputs", str(pp), str(vm), str(odr), "--pixel-pairs", str(pp)], cwd=tmp_path, @@ -27,7 +27,7 @@ def test_convert_rejects_positional_and_flag_input(tmp_path: Path): ol = tmp_path / "openlabel.json" ol.write_text(json.dumps({"openlabel": {"objects": {}, "frames": {}}}), encoding="utf-8") - # convert.py rejects positional + --input together [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) + # convert.py rejects positional + --input together res = run_cosmo(["convert", str(ol), "--input", str(ol), "--no-mcap"], cwd=tmp_path) assert res.returncode != 0 assert "Provide either a positional input OR --input/--openlabel" in (res.stderr + res.stdout) diff --git a/tests/test_cli_smoke_calibrate.py b/tests/test_cli_smoke_calibrate.py index 30da43d..423a823 100644 --- a/tests/test_cli_smoke_calibrate.py +++ b/tests/test_cli_smoke_calibrate.py @@ -14,7 +14,7 @@ def test_cli_calibrate_smoke(tmp_path: Path): Smoke test for the real CLI: cosmo calibrate --inputs pixel_pairs.csv visual_markers.csv map.xodr --json - Uses --inputs style and --json output exactly as supported by cosmo.cli.calibrate. [3](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd) + Uses --inputs style and --json output exactly as supported by cosmo.cli.calibrate. """ # Minimal input CSVs pixel_pairs_csv = tmp_path / "pixel_pairs.csv" @@ -39,7 +39,7 @@ def test_cli_calibrate_smoke(tmp_path: Path): encoding="utf-8", ) - # Dummy OpenDRIVE file (required arg in CLI contract) [3](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd) + # Dummy OpenDRIVE file (required arg in CLI contract) odr_path = tmp_path / "map.xodr" odr_path.write_text("", encoding="utf-8") @@ -78,7 +78,7 @@ def test_cli_calibrate_smoke(tmp_path: Path): payload = extract_json_from_stdout(res.stdout) - # CLI prints asdict(result) when --json is provided. [3](blob:https://www.microsoft365.com/c655cd7d-9ac4-4928-84e7-b9a8f04405bd) + # CLI prints asdict(result) when --json is provided. run_dir = Path(payload["run_dir"]) outputs_dir = Path(payload.get("outputs_dir") or payload["run_dir"]) calib_path = Path(payload["calibration_json_path"]) diff --git a/tests/test_cli_smoke_convert.py b/tests/test_cli_smoke_convert.py index 9bb1062..df7826d 100644 --- a/tests/test_cli_smoke_convert.py +++ b/tests/test_cli_smoke_convert.py @@ -14,7 +14,7 @@ def test_cli_convert_smoke_csv_only(tmp_path: Path): Smoke test for the real CLI: cosmo convert --input openlabel.json --calibration Calibration.json --no-mcap --json - Uses flags from cosmo.cli.convert and disables MCAP for CI stability. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) + Uses flags from cosmo.cli.convert and disables MCAP for CI stability. """ # Minimal OpenLABEL input (one object, one frame, rbbox) openlabel = { @@ -64,7 +64,7 @@ def test_cli_convert_smoke_csv_only(tmp_path: Path): str(out_dir), "--run-name", "smoke_convert", - "--no-mcap", # convert CLI uses BooleanOptionalAction; --no-mcap disables MCAP. [1](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) + "--no-mcap", # convert CLI uses BooleanOptionalAction; --no-mcap disables MCAP. "--json", ], cwd=tmp_path, diff --git a/tests/test_convert_alignment_flags.py b/tests/test_convert_alignment_flags.py index 36ba79a..3f52b9a 100644 --- a/tests/test_convert_alignment_flags.py +++ b/tests/test_convert_alignment_flags.py @@ -29,4 +29,4 @@ def test_convert_accepts_alignment_flags(tmp_path: Path): "--no-mcap", "--swap-xy", "--flip-x", "--flip-y", "--xy-offset", "1.0", "-2.0", "--yaw-offset-deg", "90"], cwd=tmp_path, ) - assert res.returncode == 0 # should be accepted and run successfully [3](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) + assert res.returncode == 0 # should be accepted and run successfully diff --git a/tests/test_convert_cli_validation.py b/tests/test_convert_cli_validation.py index d9d8f6e..ce24958 100644 --- a/tests/test_convert_cli_validation.py +++ b/tests/test_convert_cli_validation.py @@ -16,4 +16,4 @@ def test_convert_rejects_positional_and_flag_input(tmp_path: Path): cwd=tmp_path, ) assert res.returncode != 0 - assert "Provide either a positional input OR --input/--openlabel" in (res.stderr + res.stdout) # emitted by ap.error [3](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) + assert "Provide either a positional input OR --input/--openlabel" in (res.stderr + res.stdout) # emitted by ap.error diff --git a/tests/test_convert_outputs.py b/tests/test_convert_outputs.py index a778def..6aa8355 100644 --- a/tests/test_convert_outputs.py +++ b/tests/test_convert_outputs.py @@ -32,4 +32,4 @@ def test_convert_no_csv_reports_no_csv_path(tmp_path: Path): ) assert res.returncode == 0 payload = extract_json_from_stdout(res.stdout) - assert payload.get("csv_path") in (None, "", False) # run_convert should reflect csv disabled [3](blob:https://www.microsoft365.com/3377b2c7-ce90-4a5f-bcf4-96fccf281101) + assert payload.get("csv_path") in (None, "", False) # run_convert should reflect csv disabled