cloudanalyzer 0.4.0__tar.gz → 0.5.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {cloudanalyzer-0.4.0/cloudanalyzer.egg-info → cloudanalyzer-0.5.0}/PKG-INFO +13 -2
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/__init__.py +1 -1
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/batch.py +9 -1
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/benchmark.py +263 -1
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/__init__.py +12 -0
- cloudanalyzer-0.5.0/ca/core/bag_ingest.py +323 -0
- cloudanalyzer-0.5.0/ca/core/cameras.py +306 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/check_triage.py +74 -2
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/checks.py +712 -30
- cloudanalyzer-0.5.0/ca/core/gate.py +133 -0
- cloudanalyzer-0.5.0/ca/core/gs_renderer.py +205 -0
- cloudanalyzer-0.5.0/ca/core/image_evaluate.py +472 -0
- cloudanalyzer-0.5.0/ca/core/map_evaluate.py +672 -0
- cloudanalyzer-0.5.0/ca/core/plane_consistency.py +214 -0
- cloudanalyzer-0.5.0/ca/core/rendered_evaluate.py +287 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/slam_run.py +156 -8
- cloudanalyzer-0.5.0/ca/core/uncertainty_evaluate.py +131 -0
- cloudanalyzer-0.5.0/ca/experiments/bag_ingest/__init__.py +10 -0
- cloudanalyzer-0.5.0/ca/experiments/bag_ingest/common.py +21 -0
- cloudanalyzer-0.5.0/ca/experiments/bag_ingest/extract_all.py +10 -0
- cloudanalyzer-0.5.0/ca/experiments/bag_ingest/pointcloud.py +7 -0
- cloudanalyzer-0.5.0/ca/experiments/bag_ingest/stream_decode.py +10 -0
- cloudanalyzer-0.5.0/ca/experiments/bag_ingest/trajectory.py +7 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/slam_run/kiss_icp_driver.py +2 -1
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/info.py +8 -8
- cloudanalyzer-0.5.0/ca/io.py +440 -0
- cloudanalyzer-0.5.0/ca/leaderboard.py +373 -0
- cloudanalyzer-0.5.0/ca/mapeval_benchmark.py +150 -0
- cloudanalyzer-0.5.0/ca/mapeval_parity.py +778 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/object_eval.py +7 -2
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/pr_comment.py +60 -10
- cloudanalyzer-0.5.0/ca/protocol.py +261 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/report.py +612 -10
- cloudanalyzer-0.5.0/ca/report_paths.py +42 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/run_evaluate.py +16 -0
- cloudanalyzer-0.5.0/ca/testing/__init__.py +5 -0
- cloudanalyzer-0.5.0/ca/testing/conformance.py +236 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/trajectory.py +417 -14
- cloudanalyzer-0.5.0/ca/visualization.py +174 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0/cloudanalyzer.egg-info}/PKG-INFO +13 -2
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/cloudanalyzer.egg-info/SOURCES.txt +40 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/cloudanalyzer.egg-info/requires.txt +15 -1
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/cloudanalyzer_cli/main.py +965 -56
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/pyproject.toml +15 -1
- cloudanalyzer-0.5.0/tests/test_bag_ingest.py +329 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_benchmark.py +113 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_bundle.py +19 -15
- cloudanalyzer-0.5.0/tests/test_cameras.py +96 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_check_suite.py +85 -0
- cloudanalyzer-0.5.0/tests/test_check_suite_image.py +258 -0
- cloudanalyzer-0.5.0/tests/test_check_suite_rendered.py +300 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_cli.py +106 -0
- cloudanalyzer-0.5.0/tests/test_docs_golden_path.py +59 -0
- cloudanalyzer-0.5.0/tests/test_driver_conformance.py +102 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_geometry.py +9 -1
- cloudanalyzer-0.5.0/tests/test_image_evaluate.py +338 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_io.py +48 -1
- cloudanalyzer-0.5.0/tests/test_leaderboard.py +150 -0
- cloudanalyzer-0.5.0/tests/test_map_evaluate_core.py +206 -0
- cloudanalyzer-0.5.0/tests/test_mapeval_benchmark.py +36 -0
- cloudanalyzer-0.5.0/tests/test_mapeval_parity.py +72 -0
- cloudanalyzer-0.5.0/tests/test_phase35_map_metrics.py +86 -0
- cloudanalyzer-0.5.0/tests/test_plane_consistency.py +139 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_pr_comment.py +12 -16
- cloudanalyzer-0.5.0/tests/test_protocol.py +174 -0
- cloudanalyzer-0.5.0/tests/test_public_artifacts.py +70 -0
- cloudanalyzer-0.5.0/tests/test_rendered_evaluate.py +109 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_report.py +8 -0
- cloudanalyzer-0.5.0/tests/test_report_paths.py +49 -0
- cloudanalyzer-0.5.0/tests/test_slam_run_plugin_example.py +104 -0
- cloudanalyzer-0.5.0/tests/test_slam_run_registry.py +295 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_traj_batch.py +52 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_trajectory.py +87 -0
- cloudanalyzer-0.5.0/tests/test_uncertainty_evaluate.py +127 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_visualization.py +13 -0
- cloudanalyzer-0.4.0/ca/core/map_evaluate.py +0 -302
- cloudanalyzer-0.4.0/ca/io.py +0 -138
- cloudanalyzer-0.4.0/ca/visualization.py +0 -65
- cloudanalyzer-0.4.0/tests/test_map_evaluate_core.py +0 -81
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/LICENSE +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/README.md +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/align.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/baseline_history.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/bundle.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/compare.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/convert.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/check_baseline_evolution.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/check_scaffolding.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/ground_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/web_progressive_loading.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/web_sampling.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/core/web_trajectory_sampling.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/crop.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/density_map.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/detection.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/diff.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/downsample.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_baseline_evolution/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_baseline_evolution/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_baseline_evolution/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_baseline_evolution/pareto_promote.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_baseline_evolution/stability_window.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_baseline_evolution/threshold_guard.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_scaffolding/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_scaffolding/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_scaffolding/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_scaffolding/literal_profiles.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_scaffolding/object_sections.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_scaffolding/pipeline_overlays.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_triage/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_triage/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_triage/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_triage/pareto_frontier.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_triage/severity_weighted.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/check_triage/signature_cluster.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/ground_evaluate/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/ground_evaluate/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/ground_evaluate/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/ground_evaluate/height_band.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/ground_evaluate/nearest_neighbor.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/ground_evaluate/voxel_confusion.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/map_evaluate/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/map_evaluate/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/map_evaluate/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/map_evaluate/nn_thresholds.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/map_evaluate/voxel_entropy.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/process_docs.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/slam_run/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/slam_run/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/slam_run/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/slam_run/identity_passthrough.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/slam_run/kiss_slam_driver.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/slam_run/small_gicp_driver.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_progressive_loading/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_progressive_loading/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_progressive_loading/distance_shells.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_progressive_loading/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_progressive_loading/grid_tiles.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_progressive_loading/spatial_shuffle.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_sampling/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_sampling/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_sampling/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_sampling/functional_voxel.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_sampling/object_random.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_sampling/pipeline_hybrid.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_trajectory_sampling/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_trajectory_sampling/common.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_trajectory_sampling/distance_accumulator.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_trajectory_sampling/evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_trajectory_sampling/turn_aware.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/experiments/web_trajectory_sampling/uniform_stride.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/filter.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/geometry.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/ground_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/history.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/kitti.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/log.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/loop_closure_report.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/merge.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/metrics.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/mme.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/normals.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/pareto.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/pipeline.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/plot.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/point_summary.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/posegraph.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/registration.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/sample.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/scan_match_debug.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/slam_debug.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/split.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/stats.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/tracking.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/view.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/ca/web.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/cloudanalyzer.egg-info/dependency_links.txt +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/cloudanalyzer.egg-info/entry_points.txt +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/cloudanalyzer.egg-info/top_level.txt +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/cloudanalyzer_cli/__init__.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/setup.cfg +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/setup.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_align.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_baseline_history.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_batch.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_check_baseline_evolution_process.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_check_scaffolding_process.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_check_triage_process.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_compare.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_convert.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_crop.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_density_map.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_detection_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_detection_tracking_report.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_diff.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_downsample.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_filter.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_geometry_splat_ellipsoid.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_ground_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_ground_evaluate_process.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_history.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_info.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_kitti.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_logging.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_loop_closure_report.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_map_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_merge.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_metrics.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_mme.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_normals.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_object_eval_iou.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_output_json.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_pareto.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_phase16_large_scale.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_phase17_vectorize.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_phase18_vectorize.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_pipeline.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_plot.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_posegraph.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_process_docs.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_public_benchmark_pack.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_public_object_eval_examples.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_registration.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_run_batch.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_run_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_sample.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_scan_match_debug.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_slam_debug.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_slam_run.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_split.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_stats.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_threshold.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_tracking_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_progressive_loading_core.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_progressive_loading_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_progressive_loading_process.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_progressive_loading_strategies.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_sampling_core.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_sampling_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_sampling_process.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_sampling_strategies.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_trajectory_sampling_core.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_trajectory_sampling_evaluate.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_trajectory_sampling_process.py +0 -0
- {cloudanalyzer-0.4.0 → cloudanalyzer-0.5.0}/tests/test_web_trajectory_sampling_strategies.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cloudanalyzer
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.0
|
|
4
4
|
Summary: CLI-first QA toolkit for point cloud, trajectory, and 3D perception outputs
|
|
5
5
|
Author: rsasaki0109
|
|
6
6
|
License-Expression: MIT
|
|
@@ -22,7 +22,7 @@ Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
|
22
22
|
Requires-Python: >=3.10
|
|
23
23
|
Description-Content-Type: text/markdown
|
|
24
24
|
License-File: LICENSE
|
|
25
|
-
Requires-Dist: open3d
|
|
25
|
+
Requires-Dist: open3d<0.20,>=0.17.0
|
|
26
26
|
Requires-Dist: numpy>=1.24.0
|
|
27
27
|
Requires-Dist: typer>=0.9.0
|
|
28
28
|
Requires-Dist: matplotlib>=3.7.0
|
|
@@ -40,6 +40,17 @@ Provides-Extra: slam
|
|
|
40
40
|
Requires-Dist: kiss-icp>=1.2.0; extra == "slam"
|
|
41
41
|
Requires-Dist: kiss-slam>=0.0.2; extra == "slam"
|
|
42
42
|
Requires-Dist: small_gicp>=1.0.0; extra == "slam"
|
|
43
|
+
Provides-Extra: ros
|
|
44
|
+
Requires-Dist: rosbags>=0.10; extra == "ros"
|
|
45
|
+
Provides-Extra: gs
|
|
46
|
+
Requires-Dist: torch>=2.0.0; extra == "gs"
|
|
47
|
+
Requires-Dist: gsplat>=1.0.0; extra == "gs"
|
|
48
|
+
Requires-Dist: lpips>=0.1.4; extra == "gs"
|
|
49
|
+
Requires-Dist: dreamsim>=0.2.1; extra == "gs"
|
|
50
|
+
Provides-Extra: perceptual
|
|
51
|
+
Requires-Dist: torch>=2.0.0; extra == "perceptual"
|
|
52
|
+
Requires-Dist: lpips>=0.1.4; extra == "perceptual"
|
|
53
|
+
Requires-Dist: dreamsim>=0.2.1; extra == "perceptual"
|
|
43
54
|
Dynamic: license-file
|
|
44
55
|
|
|
45
56
|
# CloudAnalyzer
|
|
@@ -5,7 +5,6 @@ from pathlib import Path
|
|
|
5
5
|
|
|
6
6
|
from ca.evaluate import evaluate
|
|
7
7
|
from ca.io import SUPPORTED_EXTENSIONS, load_point_cloud
|
|
8
|
-
from ca.info import get_info
|
|
9
8
|
from ca.log import logger
|
|
10
9
|
from ca.pareto import mark_quality_size_recommended
|
|
11
10
|
from ca.trajectory import SUPPORTED_TRAJECTORY_EXTENSIONS, evaluate_trajectory
|
|
@@ -216,6 +215,8 @@ def batch_info(directory: str, recursive: bool = False) -> list[dict]:
|
|
|
216
215
|
Returns:
|
|
217
216
|
List of info dicts for each file found.
|
|
218
217
|
"""
|
|
218
|
+
from ca.info import get_info
|
|
219
|
+
|
|
219
220
|
files = _find_point_cloud_files(directory, recursive=recursive)
|
|
220
221
|
if not files:
|
|
221
222
|
return []
|
|
@@ -331,8 +332,10 @@ def trajectory_batch_evaluate(
|
|
|
331
332
|
align_rigid: bool = False,
|
|
332
333
|
max_ate: float | None = None,
|
|
333
334
|
max_rpe: float | None = None,
|
|
335
|
+
max_rpe_rotation_deg: float | None = None,
|
|
334
336
|
max_drift: float | None = None,
|
|
335
337
|
min_coverage: float | None = None,
|
|
338
|
+
rpe_distances_m: tuple[float, ...] | list[float] | None = None,
|
|
336
339
|
) -> list[dict]:
|
|
337
340
|
"""Evaluate all trajectory files in a directory against matched references."""
|
|
338
341
|
files = _find_trajectory_files(directory, recursive=recursive)
|
|
@@ -367,8 +370,10 @@ def trajectory_batch_evaluate(
|
|
|
367
370
|
align_rigid=align_rigid,
|
|
368
371
|
max_ate=max_ate,
|
|
369
372
|
max_rpe=max_rpe,
|
|
373
|
+
max_rpe_rotation_deg=max_rpe_rotation_deg,
|
|
370
374
|
max_drift=max_drift,
|
|
371
375
|
min_coverage=min_coverage,
|
|
376
|
+
rpe_distances_m=rpe_distances_m,
|
|
372
377
|
)
|
|
373
378
|
results.append(
|
|
374
379
|
{
|
|
@@ -382,6 +387,9 @@ def trajectory_batch_evaluate(
|
|
|
382
387
|
"coverage_ratio": eval_result["matching"]["coverage_ratio"],
|
|
383
388
|
"ate": eval_result["ate"],
|
|
384
389
|
"rpe_translation": eval_result["rpe_translation"],
|
|
390
|
+
"ate_rotation": eval_result["ate_rotation"],
|
|
391
|
+
"rpe_rotation": eval_result["rpe_rotation"],
|
|
392
|
+
"rpe_distance": eval_result["rpe_distance"],
|
|
385
393
|
"drift": eval_result["drift"],
|
|
386
394
|
"quality_gate": eval_result["quality_gate"],
|
|
387
395
|
"inspect": _trajectory_inspection_commands(str(f), str(reference_path)),
|
|
@@ -36,12 +36,17 @@ All file paths in the manifest are resolved relative to the manifest file.
|
|
|
36
36
|
|
|
37
37
|
from __future__ import annotations
|
|
38
38
|
|
|
39
|
+
import hashlib
|
|
40
|
+
import json
|
|
41
|
+
import platform
|
|
39
42
|
from dataclasses import dataclass, field
|
|
40
43
|
from pathlib import Path
|
|
41
44
|
from typing import Any, Mapping
|
|
42
45
|
|
|
43
46
|
import yaml # type: ignore[import-untyped]
|
|
44
47
|
|
|
48
|
+
import ca
|
|
49
|
+
from ca.report_paths import make_paths_portable
|
|
45
50
|
from ca.run_evaluate import evaluate_run
|
|
46
51
|
|
|
47
52
|
|
|
@@ -50,10 +55,14 @@ GATE_KEYS: tuple[str, ...] = (
|
|
|
50
55
|
"max_chamfer",
|
|
51
56
|
"max_ate",
|
|
52
57
|
"max_rpe",
|
|
58
|
+
"max_rpe_rotation_deg",
|
|
53
59
|
"max_drift",
|
|
54
60
|
"min_coverage",
|
|
55
61
|
)
|
|
56
62
|
|
|
63
|
+
REPORT_BUNDLE_SCHEMA_VERSION = "cloudanalyzer.benchmark_report_bundle.v0.1"
|
|
64
|
+
METRICS_SCHEMA_VERSION = "cloudanalyzer.metrics.v1"
|
|
65
|
+
|
|
57
66
|
|
|
58
67
|
@dataclass(frozen=True, slots=True)
|
|
59
68
|
class BenchmarkSequence:
|
|
@@ -72,6 +81,7 @@ class BenchmarkSuite:
|
|
|
72
81
|
description: str
|
|
73
82
|
license: str | None
|
|
74
83
|
source_path: Path
|
|
84
|
+
dataset: dict[str, Any] = field(default_factory=dict)
|
|
75
85
|
sequences: dict[str, BenchmarkSequence] = field(default_factory=dict)
|
|
76
86
|
gate: dict[str, float] = field(default_factory=dict)
|
|
77
87
|
|
|
@@ -205,6 +215,7 @@ def load_benchmark_suite(path: str | Path) -> BenchmarkSuite:
|
|
|
205
215
|
)
|
|
206
216
|
|
|
207
217
|
base_dir = manifest_path.parent
|
|
218
|
+
dataset = dict(_as_mapping(config.get("dataset"), "dataset"))
|
|
208
219
|
sequences = _parse_sequences(
|
|
209
220
|
config.get("sequences"),
|
|
210
221
|
config.get("sample_outputs"),
|
|
@@ -220,6 +231,7 @@ def load_benchmark_suite(path: str | Path) -> BenchmarkSuite:
|
|
|
220
231
|
sequences=sequences,
|
|
221
232
|
gate=gate,
|
|
222
233
|
source_path=manifest_path,
|
|
234
|
+
dataset=dataset,
|
|
223
235
|
)
|
|
224
236
|
|
|
225
237
|
|
|
@@ -271,22 +283,268 @@ def evaluate_benchmark_run(
|
|
|
271
283
|
max_time_delta=max_time_delta,
|
|
272
284
|
align_origin=align_origin,
|
|
273
285
|
align_rigid=align_rigid,
|
|
274
|
-
|
|
286
|
+
min_auc=gate.get("min_auc"),
|
|
287
|
+
max_chamfer=gate.get("max_chamfer"),
|
|
288
|
+
max_ate=gate.get("max_ate"),
|
|
289
|
+
max_rpe=gate.get("max_rpe"),
|
|
290
|
+
max_rpe_rotation_deg=gate.get("max_rpe_rotation_deg"),
|
|
291
|
+
max_drift=gate.get("max_drift"),
|
|
292
|
+
min_coverage=gate.get("min_coverage"),
|
|
275
293
|
)
|
|
276
294
|
result["benchmark"] = {
|
|
277
295
|
"suite": suite.name,
|
|
278
296
|
"version": suite.version,
|
|
279
297
|
"sequence": seq.name,
|
|
280
298
|
"source_path": str(suite.source_path),
|
|
299
|
+
"dataset": dict(suite.dataset),
|
|
281
300
|
"gate": gate,
|
|
282
301
|
"reference": {
|
|
283
302
|
"map": str(seq.reference_map_path),
|
|
284
303
|
"trajectory": str(seq.reference_trajectory_path),
|
|
285
304
|
},
|
|
286
305
|
}
|
|
306
|
+
result["schema_version"] = METRICS_SCHEMA_VERSION
|
|
307
|
+
result["runtime"] = {
|
|
308
|
+
"python": platform.python_version(),
|
|
309
|
+
"cloudanalyzer": getattr(ca, "__version__", "0.0.0"),
|
|
310
|
+
}
|
|
287
311
|
return result
|
|
288
312
|
|
|
289
313
|
|
|
314
|
+
# ----------------------------------------------------------- report bundle
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _sha256_file(path: Path) -> str:
|
|
318
|
+
digest = hashlib.sha256()
|
|
319
|
+
with path.open("rb") as fp:
|
|
320
|
+
for chunk in iter(lambda: fp.read(1024 * 1024), b""):
|
|
321
|
+
digest.update(chunk)
|
|
322
|
+
return digest.hexdigest()
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
def _file_provenance(path: str | Path, roots: tuple[Path, ...]) -> dict[str, Any]:
|
|
326
|
+
resolved = Path(path).resolve()
|
|
327
|
+
portable_path = make_paths_portable(str(resolved), roots)
|
|
328
|
+
return {
|
|
329
|
+
"path": portable_path,
|
|
330
|
+
"sha256": _sha256_file(resolved),
|
|
331
|
+
"size_bytes": resolved.stat().st_size,
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
|
|
335
|
+
def _bundle_roots(
|
|
336
|
+
suite: BenchmarkSuite,
|
|
337
|
+
output_dir: Path,
|
|
338
|
+
extra_roots: tuple[Path | str, ...] = (),
|
|
339
|
+
) -> tuple[Path, ...]:
|
|
340
|
+
roots: list[Path] = []
|
|
341
|
+
for candidate in (
|
|
342
|
+
Path.cwd(),
|
|
343
|
+
suite.source_path.parent,
|
|
344
|
+
output_dir.parent,
|
|
345
|
+
*extra_roots,
|
|
346
|
+
):
|
|
347
|
+
path = Path(candidate).resolve()
|
|
348
|
+
if path not in roots:
|
|
349
|
+
roots.append(path)
|
|
350
|
+
return tuple(roots)
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _bundle_lock_payload(
|
|
354
|
+
*,
|
|
355
|
+
suite: BenchmarkSuite,
|
|
356
|
+
sequence: BenchmarkSequence,
|
|
357
|
+
result: Mapping[str, Any],
|
|
358
|
+
output_dir: Path,
|
|
359
|
+
map_path: str | Path,
|
|
360
|
+
trajectory_path: str | Path,
|
|
361
|
+
report_assets: list[str],
|
|
362
|
+
roots: tuple[Path, ...],
|
|
363
|
+
) -> dict[str, Any]:
|
|
364
|
+
benchmark = result.get("benchmark")
|
|
365
|
+
benchmark_map = benchmark if isinstance(benchmark, Mapping) else {}
|
|
366
|
+
return {
|
|
367
|
+
"schema_version": REPORT_BUNDLE_SCHEMA_VERSION,
|
|
368
|
+
"metrics_schema_version": METRICS_SCHEMA_VERSION,
|
|
369
|
+
"suite": {
|
|
370
|
+
"name": suite.name,
|
|
371
|
+
"version": suite.version,
|
|
372
|
+
"description": suite.description,
|
|
373
|
+
"license": suite.license,
|
|
374
|
+
"dataset": dict(suite.dataset),
|
|
375
|
+
"sequence": sequence.name,
|
|
376
|
+
"source_path": make_paths_portable(str(suite.source_path), roots),
|
|
377
|
+
},
|
|
378
|
+
"gate": dict(benchmark_map.get("gate", suite.gate)),
|
|
379
|
+
"inputs": {
|
|
380
|
+
"candidate_map": _file_provenance(map_path, roots),
|
|
381
|
+
"candidate_trajectory": _file_provenance(trajectory_path, roots),
|
|
382
|
+
"reference_map": _file_provenance(sequence.reference_map_path, roots),
|
|
383
|
+
"reference_trajectory": _file_provenance(
|
|
384
|
+
sequence.reference_trajectory_path,
|
|
385
|
+
roots,
|
|
386
|
+
),
|
|
387
|
+
"suite_manifest": _file_provenance(suite.source_path, roots),
|
|
388
|
+
},
|
|
389
|
+
"outputs": {
|
|
390
|
+
"metrics": "metrics.json",
|
|
391
|
+
"summary": "summary.md",
|
|
392
|
+
"report": "report.html",
|
|
393
|
+
"report_assets": report_assets,
|
|
394
|
+
"provenance": "provenance.json",
|
|
395
|
+
"manifest_lock": "manifest.lock.yaml",
|
|
396
|
+
},
|
|
397
|
+
"bundle_path": make_paths_portable(str(output_dir), roots),
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
def write_benchmark_report_bundle(
|
|
402
|
+
result: Mapping[str, Any],
|
|
403
|
+
suite: BenchmarkSuite,
|
|
404
|
+
output_dir: str | Path,
|
|
405
|
+
*,
|
|
406
|
+
map_path: str | Path,
|
|
407
|
+
trajectory_path: str | Path,
|
|
408
|
+
sequence: str | None = None,
|
|
409
|
+
extra_roots: tuple[Path | str, ...] = (),
|
|
410
|
+
) -> dict[str, str]:
|
|
411
|
+
"""Write the standard ``ca benchmark eval --out`` report bundle.
|
|
412
|
+
|
|
413
|
+
The bundle is a directory contract intended for CI artifacts and static
|
|
414
|
+
publishing. It is intentionally simple and inspectable:
|
|
415
|
+
|
|
416
|
+
- ``metrics.json``: portable benchmark result JSON
|
|
417
|
+
- ``summary.md``: PR-comment-ready Markdown summary
|
|
418
|
+
- ``report.html``: human report
|
|
419
|
+
- ``manifest.lock.yaml``: suite/gate/input hash lock
|
|
420
|
+
- ``provenance.json``: machine-readable bundle metadata
|
|
421
|
+
"""
|
|
422
|
+
out = Path(output_dir)
|
|
423
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
424
|
+
roots = _bundle_roots(suite, out.resolve(), extra_roots=extra_roots)
|
|
425
|
+
sequence_name = sequence
|
|
426
|
+
benchmark = result.get("benchmark")
|
|
427
|
+
if sequence_name is None and isinstance(benchmark, Mapping):
|
|
428
|
+
raw_sequence = benchmark.get("sequence")
|
|
429
|
+
if isinstance(raw_sequence, str):
|
|
430
|
+
sequence_name = raw_sequence
|
|
431
|
+
seq = suite.resolve_sequence(sequence_name)
|
|
432
|
+
|
|
433
|
+
portable_result = make_paths_portable(dict(result), roots)
|
|
434
|
+
metrics_path = out / "metrics.json"
|
|
435
|
+
metrics_path.write_text(
|
|
436
|
+
json.dumps(portable_result, indent=2),
|
|
437
|
+
encoding="utf-8",
|
|
438
|
+
)
|
|
439
|
+
|
|
440
|
+
from ca.pr_comment import build_pr_comment # local import avoids cold CLI cycles
|
|
441
|
+
from ca.report import save_run_report
|
|
442
|
+
|
|
443
|
+
summary_path = out / "summary.md"
|
|
444
|
+
summary_path.write_text(build_pr_comment(portable_result), encoding="utf-8")
|
|
445
|
+
|
|
446
|
+
report_path = out / "report.html"
|
|
447
|
+
save_run_report(portable_result, str(report_path))
|
|
448
|
+
report_assets = sorted(
|
|
449
|
+
path.name
|
|
450
|
+
for path in out.iterdir()
|
|
451
|
+
if path.is_file()
|
|
452
|
+
and path.name
|
|
453
|
+
not in {
|
|
454
|
+
"manifest.lock.yaml",
|
|
455
|
+
"metrics.json",
|
|
456
|
+
"provenance.json",
|
|
457
|
+
"report.html",
|
|
458
|
+
"summary.md",
|
|
459
|
+
}
|
|
460
|
+
)
|
|
461
|
+
|
|
462
|
+
lock_payload = _bundle_lock_payload(
|
|
463
|
+
suite=suite,
|
|
464
|
+
sequence=seq,
|
|
465
|
+
result=portable_result,
|
|
466
|
+
output_dir=out.resolve(),
|
|
467
|
+
map_path=map_path,
|
|
468
|
+
trajectory_path=trajectory_path,
|
|
469
|
+
report_assets=report_assets,
|
|
470
|
+
roots=roots,
|
|
471
|
+
)
|
|
472
|
+
manifest_lock_path = out / "manifest.lock.yaml"
|
|
473
|
+
manifest_lock_path.write_text(
|
|
474
|
+
yaml.safe_dump(lock_payload, sort_keys=False),
|
|
475
|
+
encoding="utf-8",
|
|
476
|
+
)
|
|
477
|
+
|
|
478
|
+
provenance = {
|
|
479
|
+
"schema_version": REPORT_BUNDLE_SCHEMA_VERSION,
|
|
480
|
+
"metrics_schema_version": METRICS_SCHEMA_VERSION,
|
|
481
|
+
"cloudanalyzer_version": getattr(ca, "__version__", "0.0.0"),
|
|
482
|
+
"summary_kind": "benchmark_run",
|
|
483
|
+
"benchmark": portable_result.get("benchmark"),
|
|
484
|
+
"overall_quality_gate": portable_result.get("overall_quality_gate"),
|
|
485
|
+
"artifacts": lock_payload["outputs"],
|
|
486
|
+
"inputs": lock_payload["inputs"],
|
|
487
|
+
}
|
|
488
|
+
provenance_path = out / "provenance.json"
|
|
489
|
+
provenance_path.write_text(
|
|
490
|
+
json.dumps(provenance, indent=2),
|
|
491
|
+
encoding="utf-8",
|
|
492
|
+
)
|
|
493
|
+
|
|
494
|
+
return {
|
|
495
|
+
"bundle_dir": str(out),
|
|
496
|
+
"metrics": str(metrics_path),
|
|
497
|
+
"summary": str(summary_path),
|
|
498
|
+
"report": str(report_path),
|
|
499
|
+
"manifest_lock": str(manifest_lock_path),
|
|
500
|
+
"provenance": str(provenance_path),
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
def validate_benchmark_report_bundle(output_dir: str | Path) -> dict[str, Any]:
|
|
505
|
+
"""Validate the local shape of a benchmark report bundle.
|
|
506
|
+
|
|
507
|
+
This checks the machine-readable schema/version and referenced report
|
|
508
|
+
assets. It deliberately does not resolve portable input paths outside the
|
|
509
|
+
bundle; input hashes remain the authoritative provenance for that step.
|
|
510
|
+
"""
|
|
511
|
+
root = Path(output_dir)
|
|
512
|
+
required = {
|
|
513
|
+
"metrics": root / "metrics.json",
|
|
514
|
+
"summary": root / "summary.md",
|
|
515
|
+
"report": root / "report.html",
|
|
516
|
+
"manifest_lock": root / "manifest.lock.yaml",
|
|
517
|
+
"provenance": root / "provenance.json",
|
|
518
|
+
}
|
|
519
|
+
missing = [name for name, path in required.items() if not path.is_file()]
|
|
520
|
+
if missing:
|
|
521
|
+
raise ValueError(f"Benchmark report bundle is missing: {', '.join(missing)}")
|
|
522
|
+
|
|
523
|
+
metrics = json.loads(required["metrics"].read_text(encoding="utf-8"))
|
|
524
|
+
provenance = json.loads(required["provenance"].read_text(encoding="utf-8"))
|
|
525
|
+
lock = yaml.safe_load(required["manifest_lock"].read_text(encoding="utf-8"))
|
|
526
|
+
if metrics.get("schema_version") != METRICS_SCHEMA_VERSION:
|
|
527
|
+
raise ValueError("metrics.json has an unsupported schema_version")
|
|
528
|
+
if provenance.get("metrics_schema_version") != METRICS_SCHEMA_VERSION:
|
|
529
|
+
raise ValueError("provenance.json has an unsupported metrics_schema_version")
|
|
530
|
+
if lock.get("schema_version") != REPORT_BUNDLE_SCHEMA_VERSION:
|
|
531
|
+
raise ValueError("manifest.lock.yaml has an unsupported schema_version")
|
|
532
|
+
|
|
533
|
+
report_assets = lock.get("outputs", {}).get("report_assets", [])
|
|
534
|
+
missing_assets = [name for name in report_assets if not (root / name).is_file()]
|
|
535
|
+
if missing_assets:
|
|
536
|
+
raise ValueError(
|
|
537
|
+
"Benchmark report bundle is missing report assets: "
|
|
538
|
+
+ ", ".join(missing_assets)
|
|
539
|
+
)
|
|
540
|
+
return {
|
|
541
|
+
"valid": True,
|
|
542
|
+
"schema_version": REPORT_BUNDLE_SCHEMA_VERSION,
|
|
543
|
+
"metrics_schema_version": METRICS_SCHEMA_VERSION,
|
|
544
|
+
"report_assets": list(report_assets),
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
|
|
290
548
|
# ----------------------------------------------------------- materialize
|
|
291
549
|
|
|
292
550
|
|
|
@@ -425,7 +683,11 @@ __all__: list[str] = [
|
|
|
425
683
|
"BenchmarkSequence",
|
|
426
684
|
"BenchmarkSuite",
|
|
427
685
|
"GATE_KEYS",
|
|
686
|
+
"METRICS_SCHEMA_VERSION",
|
|
687
|
+
"REPORT_BUNDLE_SCHEMA_VERSION",
|
|
428
688
|
"evaluate_benchmark_run",
|
|
429
689
|
"load_benchmark_suite",
|
|
430
690
|
"materialize_suite",
|
|
691
|
+
"write_benchmark_report_bundle",
|
|
692
|
+
"validate_benchmark_report_bundle",
|
|
431
693
|
]
|
|
@@ -33,6 +33,13 @@ from ca.core.check_triage import (
|
|
|
33
33
|
rank_failed_checks,
|
|
34
34
|
summarize_failed_checks,
|
|
35
35
|
)
|
|
36
|
+
from ca.core.gate import (
|
|
37
|
+
GATE_SEVERITIES,
|
|
38
|
+
GATE_SUMMARY_SCHEMA_VERSION,
|
|
39
|
+
gate_status_for_check,
|
|
40
|
+
normalize_gate_severity,
|
|
41
|
+
summarize_gate_policy,
|
|
42
|
+
)
|
|
36
43
|
from ca.core.ground_evaluate import (
|
|
37
44
|
GroundEvaluateRequest,
|
|
38
45
|
GroundEvaluateResult,
|
|
@@ -90,6 +97,8 @@ __all__ = [
|
|
|
90
97
|
"GroundEvaluateRequest",
|
|
91
98
|
"GroundEvaluateResult",
|
|
92
99
|
"GroundEvaluateStrategy",
|
|
100
|
+
"GATE_SEVERITIES",
|
|
101
|
+
"GATE_SUMMARY_SCHEMA_VERSION",
|
|
93
102
|
"DistanceShellsWebProgressiveLoadingStrategy",
|
|
94
103
|
"RandomBudgetWebSamplingStrategy",
|
|
95
104
|
"RankedCheckTriageItem",
|
|
@@ -114,7 +123,9 @@ __all__ = [
|
|
|
114
123
|
"decide_baseline_evolution",
|
|
115
124
|
"evaluate_ground",
|
|
116
125
|
"failed_dimensions",
|
|
126
|
+
"gate_status_for_check",
|
|
117
127
|
"load_check_suite",
|
|
128
|
+
"normalize_gate_severity",
|
|
118
129
|
"normalized_dimension_gaps",
|
|
119
130
|
"plan_progressive_loading_for_web",
|
|
120
131
|
"rank_failed_checks",
|
|
@@ -126,4 +137,5 @@ __all__ = [
|
|
|
126
137
|
"snapshot_margin_stats",
|
|
127
138
|
"summarize_baseline_evolution",
|
|
128
139
|
"summarize_failed_checks",
|
|
140
|
+
"summarize_gate_policy",
|
|
129
141
|
]
|