avialsync 0.1.0b5__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.
- avialsync-0.1.0b5/.flake8 +21 -0
- avialsync-0.1.0b5/.gitattributes +5 -0
- avialsync-0.1.0b5/.githooks/post-commit +7 -0
- avialsync-0.1.0b5/.githooks/pre-commit +25 -0
- avialsync-0.1.0b5/.github/ISSUE_TEMPLATE/bug_report.yml +58 -0
- avialsync-0.1.0b5/.github/ISSUE_TEMPLATE/config.yml +5 -0
- avialsync-0.1.0b5/.github/ISSUE_TEMPLATE/feature_request.yml +37 -0
- avialsync-0.1.0b5/.github/pull_request_template.md +35 -0
- avialsync-0.1.0b5/.github/workflows/ci.yml +140 -0
- avialsync-0.1.0b5/.github/workflows/release.yml +410 -0
- avialsync-0.1.0b5/.gitignore +156 -0
- avialsync-0.1.0b5/.pre-commit-config.yaml +7 -0
- avialsync-0.1.0b5/.readthedocs.yaml +17 -0
- avialsync-0.1.0b5/AGENTS.md +227 -0
- avialsync-0.1.0b5/ARCHITECTURE.md +454 -0
- avialsync-0.1.0b5/BLUEPRINT.md +294 -0
- avialsync-0.1.0b5/CLAUDE.md +4 -0
- avialsync-0.1.0b5/CODE_OF_CONDUCT.md +129 -0
- avialsync-0.1.0b5/CONTRIBUTING.md +117 -0
- avialsync-0.1.0b5/DECISIONS.md +1732 -0
- avialsync-0.1.0b5/GEMINI.md +4 -0
- avialsync-0.1.0b5/HANDOUT.md +1005 -0
- avialsync-0.1.0b5/LICENSE +3 -0
- avialsync-0.1.0b5/PKG-INFO +236 -0
- avialsync-0.1.0b5/PLOT_UX_PLAN.md +312 -0
- avialsync-0.1.0b5/PROMPTS.md +171 -0
- avialsync-0.1.0b5/README.md +203 -0
- avialsync-0.1.0b5/RECOVERY_PLAN.md +429 -0
- avialsync-0.1.0b5/RECOVERY_PROMPT.md +593 -0
- avialsync-0.1.0b5/TESTING.md +201 -0
- avialsync-0.1.0b5/assets/avial_sync.png +0 -0
- avialsync-0.1.0b5/docs/_static/screenshots/demo_step1_empty.png +0 -0
- avialsync-0.1.0b5/docs/_static/screenshots/demo_step2_video_loaded.png +0 -0
- avialsync-0.1.0b5/docs/_static/screenshots/demo_step3_csv_loaded.png +0 -0
- avialsync-0.1.0b5/docs/_static/screenshots/demo_step4_wizard_open.png +0 -0
- avialsync-0.1.0b5/docs/_static/screenshots/demo_step5_wizard_configured.png +0 -0
- avialsync-0.1.0b5/docs/_static/screenshots/demo_step6_wizard_previewed.png +0 -0
- avialsync-0.1.0b5/docs/_static/screenshots/demo_step7_mapping_applied.png +0 -0
- avialsync-0.1.0b5/docs/conf.py +21 -0
- avialsync-0.1.0b5/docs/formats.md +23 -0
- avialsync-0.1.0b5/docs/index.rst +19 -0
- avialsync-0.1.0b5/docs/plugin-guide.md +51 -0
- avialsync-0.1.0b5/docs/quickstart.md +67 -0
- avialsync-0.1.0b5/docs/technical/architecture.md +72 -0
- avialsync-0.1.0b5/docs/technical/data-handling.md +103 -0
- avialsync-0.1.0b5/docs/technical/index.md +13 -0
- avialsync-0.1.0b5/docs/technical/performance.md +89 -0
- avialsync-0.1.0b5/docs/troubleshooting.md +31 -0
- avialsync-0.1.0b5/docs/tutorials/first-session.md +32 -0
- avialsync-0.1.0b5/docs/tutorials/synchronization.md +26 -0
- avialsync-0.1.0b5/docs/user-guide/index.md +54 -0
- avialsync-0.1.0b5/examples/data/camera_1.mp4 +0 -0
- avialsync-0.1.0b5/examples/data/camera_2.mp4 +0 -0
- avialsync-0.1.0b5/examples/data/camera_3.mp4 +0 -0
- avialsync-0.1.0b5/examples/data/camera_vfr.mp4 +0 -0
- avialsync-0.1.0b5/examples/data/mouse_wheel_run_view0.mp4 +0 -0
- avialsync-0.1.0b5/examples/data/mouse_wheel_run_view1.mp4 +0 -0
- avialsync-0.1.0b5/examples/data/mouse_wheel_run_view2.mp4 +0 -0
- avialsync-0.1.0b5/examples/data/openephys_mock/experiment1/recording1/continuous/Acquisition_Board-100.Rhythm_Data/continuous.dat +0 -0
- avialsync-0.1.0b5/examples/data/openephys_mock/experiment1/recording1/continuous/Acquisition_Board-100.Rhythm_Data/timestamps.npy +0 -0
- avialsync-0.1.0b5/examples/data/openephys_mock/experiment1/recording1/structure.oebin +50 -0
- avialsync-0.1.0b5/examples/data/pose.csv +303 -0
- avialsync-0.1.0b5/examples/data/sensors.csv +10001 -0
- avialsync-0.1.0b5/examples/data/sensors_gaps.csv +98501 -0
- avialsync-0.1.0b5/examples/data/tracking_dlc.csv +303 -0
- avialsync-0.1.0b5/examples/load_tenss_data.py +58 -0
- avialsync-0.1.0b5/examples/plugins/__init__.py +1 -0
- avialsync-0.1.0b5/examples/plugins/avialsync-plugin-example/pyproject.toml +16 -0
- avialsync-0.1.0b5/examples/plugins/avialsync-plugin-example/src/avialsync_plugin_example/__init__.py +48 -0
- avialsync-0.1.0b5/examples/profile_loading.py +27 -0
- avialsync-0.1.0b5/graphify-out/.gitignore +8 -0
- avialsync-0.1.0b5/graphify-out/GRAPH_REPORT.md +183 -0
- avialsync-0.1.0b5/graphify-out/README.md +12 -0
- avialsync-0.1.0b5/graphify-out/graph.html +39 -0
- avialsync-0.1.0b5/graphify-out/graph.json +132697 -0
- avialsync-0.1.0b5/packaging/avialsync.spec +98 -0
- avialsync-0.1.0b5/packaging/build_bundle.py +43 -0
- avialsync-0.1.0b5/packaging/conda/meta.yaml +71 -0
- avialsync-0.1.0b5/packaging/fetch_media_libs.py +138 -0
- avialsync-0.1.0b5/packaging/linux/avialsync.png +0 -0
- avialsync-0.1.0b5/packaging/linux/make_appimage.sh +31 -0
- avialsync-0.1.0b5/packaging/macos/avialsync.icns +0 -0
- avialsync-0.1.0b5/packaging/macos/make_dmg.sh +16 -0
- avialsync-0.1.0b5/packaging/macos/sign_notarize.sh +82 -0
- avialsync-0.1.0b5/packaging/probe_dialog_test.py +2 -0
- avialsync-0.1.0b5/packaging/smoke_test.py +99 -0
- avialsync-0.1.0b5/packaging/windows/avialsync.ico +0 -0
- avialsync-0.1.0b5/packaging/windows/avialsync.iss +33 -0
- avialsync-0.1.0b5/packaging/windows/sign.ps1 +60 -0
- avialsync-0.1.0b5/pyproject.toml +124 -0
- avialsync-0.1.0b5/src/avialsync/__init__.py +3 -0
- avialsync-0.1.0b5/src/avialsync/__main__.py +117 -0
- avialsync-0.1.0b5/src/avialsync/core/__init__.py +1 -0
- avialsync-0.1.0b5/src/avialsync/core/cache.py +183 -0
- avialsync-0.1.0b5/src/avialsync/core/channel_reader.py +165 -0
- avialsync-0.1.0b5/src/avialsync/core/errors.py +73 -0
- avialsync-0.1.0b5/src/avialsync/core/inspection.py +146 -0
- avialsync-0.1.0b5/src/avialsync/core/pyramid.py +508 -0
- avialsync-0.1.0b5/src/avialsync/core/registry.py +150 -0
- avialsync-0.1.0b5/src/avialsync/core/session.py +257 -0
- avialsync-0.1.0b5/src/avialsync/core/source.py +173 -0
- avialsync-0.1.0b5/src/avialsync/core/sync.py +374 -0
- avialsync-0.1.0b5/src/avialsync/core/timeline.py +299 -0
- avialsync-0.1.0b5/src/avialsync/demo.py +531 -0
- avialsync-0.1.0b5/src/avialsync/engine/__init__.py +1 -0
- avialsync-0.1.0b5/src/avialsync/engine/drop_worker.py +279 -0
- avialsync-0.1.0b5/src/avialsync/engine/export.py +227 -0
- avialsync-0.1.0b5/src/avialsync/engine/export_worker.py +200 -0
- avialsync-0.1.0b5/src/avialsync/engine/importer.py +312 -0
- avialsync-0.1.0b5/src/avialsync/engine/player.py +433 -0
- avialsync-0.1.0b5/src/avialsync/engine/proxy.py +106 -0
- avialsync-0.1.0b5/src/avialsync/engine/seeker.py +40 -0
- avialsync-0.1.0b5/src/avialsync/engine/session_worker.py +70 -0
- avialsync-0.1.0b5/src/avialsync/engine/sync_worker.py +96 -0
- avialsync-0.1.0b5/src/avialsync/engine/video_worker.py +65 -0
- avialsync-0.1.0b5/src/avialsync/loaders/__init__.py +1 -0
- avialsync-0.1.0b5/src/avialsync/loaders/aol_eks_loader.py +259 -0
- avialsync-0.1.0b5/src/avialsync/loaders/aol_encoder_loader.py +287 -0
- avialsync-0.1.0b5/src/avialsync/loaders/aol_session_loader.py +356 -0
- avialsync-0.1.0b5/src/avialsync/loaders/csv_loader.py +339 -0
- avialsync-0.1.0b5/src/avialsync/loaders/neo_loader.py +328 -0
- avialsync-0.1.0b5/src/avialsync/loaders/tracking_loader.py +152 -0
- avialsync-0.1.0b5/src/avialsync/loaders/video_standard.py +276 -0
- avialsync-0.1.0b5/src/avialsync/resources/__init__.py +1 -0
- avialsync-0.1.0b5/src/avialsync/resources/avialsync.png +0 -0
- avialsync-0.1.0b5/src/avialsync/runtime.py +161 -0
- avialsync-0.1.0b5/src/avialsync/ui/__init__.py +1 -0
- avialsync-0.1.0b5/src/avialsync/ui/annotations.py +211 -0
- avialsync-0.1.0b5/src/avialsync/ui/batch_import_dialog.py +130 -0
- avialsync-0.1.0b5/src/avialsync/ui/diagnostics.py +213 -0
- avialsync-0.1.0b5/src/avialsync/ui/import_report.py +105 -0
- avialsync-0.1.0b5/src/avialsync/ui/import_wizard.py +397 -0
- avialsync-0.1.0b5/src/avialsync/ui/job_manager.py +297 -0
- avialsync-0.1.0b5/src/avialsync/ui/main_window.py +2884 -0
- avialsync-0.1.0b5/src/avialsync/ui/offsets_panel.py +1 -0
- avialsync-0.1.0b5/src/avialsync/ui/pane_proportions.py +199 -0
- avialsync-0.1.0b5/src/avialsync/ui/plot_header.py +69 -0
- avialsync-0.1.0b5/src/avialsync/ui/plot_interactions.py +142 -0
- avialsync-0.1.0b5/src/avialsync/ui/plot_overlays.py +177 -0
- avialsync-0.1.0b5/src/avialsync/ui/plot_pane.py +812 -0
- avialsync-0.1.0b5/src/avialsync/ui/plot_row.py +274 -0
- avialsync-0.1.0b5/src/avialsync/ui/plot_sweep.py +342 -0
- avialsync-0.1.0b5/src/avialsync/ui/readout_panel.py +335 -0
- avialsync-0.1.0b5/src/avialsync/ui/recent_files.py +40 -0
- avialsync-0.1.0b5/src/avialsync/ui/relink_dialog.py +118 -0
- avialsync-0.1.0b5/src/avialsync/ui/shortcuts_dialog.py +92 -0
- avialsync-0.1.0b5/src/avialsync/ui/sidebar.py +535 -0
- avialsync-0.1.0b5/src/avialsync/ui/source_properties.py +237 -0
- avialsync-0.1.0b5/src/avialsync/ui/sync_wizard.py +219 -0
- avialsync-0.1.0b5/src/avialsync/ui/theme.py +365 -0
- avialsync-0.1.0b5/src/avialsync/ui/time_format.py +43 -0
- avialsync-0.1.0b5/src/avialsync/ui/tracking_3d_pane.py +648 -0
- avialsync-0.1.0b5/src/avialsync/ui/tracking_colors.py +111 -0
- avialsync-0.1.0b5/src/avialsync/ui/transport.py +948 -0
- avialsync-0.1.0b5/src/avialsync/ui/ui_heartbeat.py +83 -0
- avialsync-0.1.0b5/src/avialsync/ui/video_grid.py +353 -0
- avialsync-0.1.0b5/src/avialsync/ui/video_overlay.py +272 -0
- avialsync-0.1.0b5/src/avialsync/ui/video_pane.py +608 -0
- avialsync-0.1.0b5/src/avialsync/ui/video_timing.py +282 -0
- avialsync-0.1.0b5/tests/benchmarks/test_bench_plot_pane.py +167 -0
- avialsync-0.1.0b5/tests/benchmarks/test_bench_pyramid.py +218 -0
- avialsync-0.1.0b5/tests/benchmarks/test_bench_sync.py +30 -0
- avialsync-0.1.0b5/tests/benchmarks/test_bench_tracking_3d.py +59 -0
- avialsync-0.1.0b5/tests/benchmarks/test_bench_video_timing.py +67 -0
- avialsync-0.1.0b5/tests/conftest.py +1 -0
- avialsync-0.1.0b5/tests/fixtures/session_v1.avv +29 -0
- avialsync-0.1.0b5/tests/fixtures/session_v2.avv +28 -0
- avialsync-0.1.0b5/tests/fixtures/session_v3.avv +44 -0
- avialsync-0.1.0b5/tests/test_annotation_frames.py +224 -0
- avialsync-0.1.0b5/tests/test_aol_chunk_boundaries.py +168 -0
- avialsync-0.1.0b5/tests/test_aol_loaders.py +555 -0
- avialsync-0.1.0b5/tests/test_aol_pose_routing.py +290 -0
- avialsync-0.1.0b5/tests/test_axis.py +84 -0
- avialsync-0.1.0b5/tests/test_channel_identity.py +180 -0
- avialsync-0.1.0b5/tests/test_ci_platform_config.py +185 -0
- avialsync-0.1.0b5/tests/test_cli_demo.py +219 -0
- avialsync-0.1.0b5/tests/test_cli_open.py +111 -0
- avialsync-0.1.0b5/tests/test_close_and_focus.py +351 -0
- avialsync-0.1.0b5/tests/test_conda_recipe.py +69 -0
- avialsync-0.1.0b5/tests/test_core_cache.py +163 -0
- avialsync-0.1.0b5/tests/test_core_coverage_edges.py +289 -0
- avialsync-0.1.0b5/tests/test_core_edges.py +114 -0
- avialsync-0.1.0b5/tests/test_core_inspection.py +126 -0
- avialsync-0.1.0b5/tests/test_core_pyramid.py +239 -0
- avialsync-0.1.0b5/tests/test_core_registry.py +94 -0
- avialsync-0.1.0b5/tests/test_core_remaining_paths.py +448 -0
- avialsync-0.1.0b5/tests/test_core_session.py +244 -0
- avialsync-0.1.0b5/tests/test_core_sync.py +76 -0
- avialsync-0.1.0b5/tests/test_core_sync_guards.py +148 -0
- avialsync-0.1.0b5/tests/test_core_timeline.py +268 -0
- avialsync-0.1.0b5/tests/test_demo_data.py +68 -0
- avialsync-0.1.0b5/tests/test_diagnostics.py +60 -0
- avialsync-0.1.0b5/tests/test_engine_export.py +102 -0
- avialsync-0.1.0b5/tests/test_engine_importer.py +108 -0
- avialsync-0.1.0b5/tests/test_engine_layering.py +83 -0
- avialsync-0.1.0b5/tests/test_engine_sync_worker.py +34 -0
- avialsync-0.1.0b5/tests/test_exact_logic.py +81 -0
- avialsync-0.1.0b5/tests/test_fixtures_protected.py +53 -0
- avialsync-0.1.0b5/tests/test_frame_indexed.py +181 -0
- avialsync-0.1.0b5/tests/test_headless_core.py +95 -0
- avialsync-0.1.0b5/tests/test_hot_path.py +183 -0
- avialsync-0.1.0b5/tests/test_import_streaming.py +267 -0
- avialsync-0.1.0b5/tests/test_interaction_standard.py +359 -0
- avialsync-0.1.0b5/tests/test_loaders_csv.py +250 -0
- avialsync-0.1.0b5/tests/test_loaders_neo.py +112 -0
- avialsync-0.1.0b5/tests/test_loaders_tracking.py +75 -0
- avialsync-0.1.0b5/tests/test_loaders_video.py +162 -0
- avialsync-0.1.0b5/tests/test_never_freeze.py +297 -0
- avialsync-0.1.0b5/tests/test_packaging_media.py +252 -0
- avialsync-0.1.0b5/tests/test_packaging_metadata.py +14 -0
- avialsync-0.1.0b5/tests/test_packaging_smoke.py +149 -0
- avialsync-0.1.0b5/tests/test_packaging_spec.py +21 -0
- avialsync-0.1.0b5/tests/test_pane_proportions.py +209 -0
- avialsync-0.1.0b5/tests/test_playback_smoothness.py +462 -0
- avialsync-0.1.0b5/tests/test_plugin_discovery.py +135 -0
- avialsync-0.1.0b5/tests/test_prepare_release.py +42 -0
- avialsync-0.1.0b5/tests/test_pyramid_hierarchy.py +27 -0
- avialsync-0.1.0b5/tests/test_pyramid_read_api.py +181 -0
- avialsync-0.1.0b5/tests/test_runtime.py +41 -0
- avialsync-0.1.0b5/tests/test_scientific_session_workload.py +38 -0
- avialsync-0.1.0b5/tests/test_scrubbing.py +312 -0
- avialsync-0.1.0b5/tests/test_seeker.py +52 -0
- avialsync-0.1.0b5/tests/test_session_worker.py +264 -0
- avialsync-0.1.0b5/tests/test_source_time_map.py +169 -0
- avialsync-0.1.0b5/tests/test_subprocess_no_window.py +108 -0
- avialsync-0.1.0b5/tests/test_sync_golden.py +213 -0
- avialsync-0.1.0b5/tests/test_theme_tooltips.py +286 -0
- avialsync-0.1.0b5/tests/test_tracking_colors.py +98 -0
- avialsync-0.1.0b5/tests/test_transport_layout.py +236 -0
- avialsync-0.1.0b5/tests/test_transport_resize.py +116 -0
- avialsync-0.1.0b5/tests/test_typed_source_errors.py +155 -0
- avialsync-0.1.0b5/tests/test_ui_dialogs.py +278 -0
- avialsync-0.1.0b5/tests/test_ui_exact_sync.out +0 -0
- avialsync-0.1.0b5/tests/test_ui_exact_sync.py +70 -0
- avialsync-0.1.0b5/tests/test_ui_follow.py +297 -0
- avialsync-0.1.0b5/tests/test_ui_layout_resize.py +205 -0
- avialsync-0.1.0b5/tests/test_ui_main.py +583 -0
- avialsync-0.1.0b5/tests/test_ui_plot_gutter.py +83 -0
- avialsync-0.1.0b5/tests/test_ui_plot_measure.py +81 -0
- avialsync-0.1.0b5/tests/test_ui_plot_sliced_refresh.py +102 -0
- avialsync-0.1.0b5/tests/test_ui_readout_delta.py +74 -0
- avialsync-0.1.0b5/tests/test_ui_sensor_mapping.py +152 -0
- avialsync-0.1.0b5/tests/test_ui_source_properties.py +157 -0
- avialsync-0.1.0b5/tests/test_ui_sync_wizard.py +42 -0
- avialsync-0.1.0b5/tests/test_ui_time_format.py +82 -0
- avialsync-0.1.0b5/tests/test_ui_tracking_3d.py +187 -0
- avialsync-0.1.0b5/tests/test_video_grid.py +70 -0
- avialsync-0.1.0b5/tests/test_video_pane.py +118 -0
- avialsync-0.1.0b5/tests/test_video_pane_timing.py +268 -0
- avialsync-0.1.0b5/tests/test_video_worker.py +59 -0
- avialsync-0.1.0b5/tests/test_worker_lifetime.py +121 -0
- avialsync-0.1.0b5/tests/test_worker_thread_teardown.py +42 -0
- avialsync-0.1.0b5/tests/test_workload_responsiveness.py +141 -0
- avialsync-0.1.0b5/tests/util_framestrip.py +148 -0
- avialsync-0.1.0b5/tools/check_ephys_channels.py +18 -0
- avialsync-0.1.0b5/tools/check_video_fps.py +12 -0
- avialsync-0.1.0b5/tools/generate_demo_data.py +30 -0
- avialsync-0.1.0b5/tools/generate_demo_screenshots.py +133 -0
- avialsync-0.1.0b5/tools/generate_icons.py +72 -0
- avialsync-0.1.0b5/tools/generate_tenss_screenshots.py +109 -0
- avialsync-0.1.0b5/tools/launch_demo.py +9 -0
- avialsync-0.1.0b5/tools/make_fixtures.py +589 -0
- avialsync-0.1.0b5/tools/prepare_release.py +160 -0
- avialsync-0.1.0b5/tools/test_edges.py +15 -0
- avialsync-0.1.0b5/tools/test_fit.py +32 -0
- avialsync-0.1.0b5/tools/test_layout.py +25 -0
- avialsync-0.1.0b5/tools/test_neo_events.py +18 -0
- avialsync-0.1.0b5/tools/test_sync_tenss.py +89 -0
- avialsync-0.1.0b5/tools/test_sync_worker.py +44 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Ruff is this project's linter and formatter (AGENTS.md "How to run things").
|
|
2
|
+
# This file exists only so an editor-integrated flake8 agrees with it instead of
|
|
3
|
+
# reporting a wall of E501 against its own 79-column default.
|
|
4
|
+
[flake8]
|
|
5
|
+
max-line-length = 100
|
|
6
|
+
extend-ignore =
|
|
7
|
+
# Slice spacing and line breaks before binary operators are how the
|
|
8
|
+
# project's formatter writes code; flake8's defaults predate it.
|
|
9
|
+
E203,
|
|
10
|
+
W503,
|
|
11
|
+
# Ruff owns these, configured in pyproject.toml [tool.ruff.lint].
|
|
12
|
+
E501
|
|
13
|
+
exclude =
|
|
14
|
+
.git,
|
|
15
|
+
__pycache__,
|
|
16
|
+
build,
|
|
17
|
+
dist,
|
|
18
|
+
graphify-out,
|
|
19
|
+
.mypy_cache,
|
|
20
|
+
.pytest_cache,
|
|
21
|
+
.ruff_cache
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
#!/bin/sh
|
|
2
|
+
# Refuse to commit raw recording data.
|
|
3
|
+
#
|
|
4
|
+
# These directories are gitignored, but an ignore rule is not a guarantee: a
|
|
5
|
+
# `git stash` that happens to include .gitignore, or a `git add -f`, re-exposes
|
|
6
|
+
# them. Field recordings are private research data and are large; once pushed
|
|
7
|
+
# they are effectively permanent. This checks what is actually staged.
|
|
8
|
+
|
|
9
|
+
staged=$(git diff --cached --name-only --diff-filter=A)
|
|
10
|
+
[ -z "$staged" ] && exit 0
|
|
11
|
+
|
|
12
|
+
offenders=$(printf '%s\n' "$staged" | grep -E '^([0-2][0-9]-[0-5][0-9]-[0-5][0-9]|TENSS26_Anzal)/' || true)
|
|
13
|
+
|
|
14
|
+
if [ -n "$offenders" ]; then
|
|
15
|
+
printf '%s\n' 'pre-commit: refusing to commit raw recording data.' >&2
|
|
16
|
+
printf '%s\n' '' >&2
|
|
17
|
+
printf '%s\n' "$offenders" | head -20 >&2
|
|
18
|
+
count=$(printf '%s\n' "$offenders" | wc -l | tr -d ' ')
|
|
19
|
+
[ "$count" -gt 20 ] && printf ' ... and %s more\n' "$((count - 20))" >&2
|
|
20
|
+
printf '%s\n' '' >&2
|
|
21
|
+
printf '%s\n' 'Unstage them: git reset HEAD -- <path>' >&2
|
|
22
|
+
printf '%s\n' 'If this is genuinely intended, commit with --no-verify.' >&2
|
|
23
|
+
exit 1
|
|
24
|
+
fi
|
|
25
|
+
exit 0
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Something behaves incorrectly
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Please do not attach private research data. If the bug only reproduces
|
|
9
|
+
on your own recordings, describe their shape instead (rate, duration,
|
|
10
|
+
channel count, container) and we will try to build a matching fixture.
|
|
11
|
+
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: what-happened
|
|
14
|
+
attributes:
|
|
15
|
+
label: What happened
|
|
16
|
+
description: What you did, what you expected, and what actually happened.
|
|
17
|
+
placeholder: |
|
|
18
|
+
1. Opened a session with 3 cameras and a 50 kHz CSV
|
|
19
|
+
2. Dragged the seek bar to ~00:04:12 and released
|
|
20
|
+
3. Expected the plots and video to land on the same instant
|
|
21
|
+
4. The middle camera stayed one frame behind until I paused
|
|
22
|
+
validations:
|
|
23
|
+
required: true
|
|
24
|
+
|
|
25
|
+
- type: input
|
|
26
|
+
id: version
|
|
27
|
+
attributes:
|
|
28
|
+
label: AvialSync version
|
|
29
|
+
placeholder: "0.1.0b4, or a commit SHA"
|
|
30
|
+
validations:
|
|
31
|
+
required: true
|
|
32
|
+
|
|
33
|
+
- type: dropdown
|
|
34
|
+
id: os
|
|
35
|
+
attributes:
|
|
36
|
+
label: Operating system
|
|
37
|
+
options: [Windows, macOS, Linux]
|
|
38
|
+
validations:
|
|
39
|
+
required: true
|
|
40
|
+
|
|
41
|
+
- type: textarea
|
|
42
|
+
id: diagnostics
|
|
43
|
+
attributes:
|
|
44
|
+
label: Diagnostics
|
|
45
|
+
description: >
|
|
46
|
+
Help → Diagnostics → "Copy diagnostics", pasted here. This carries your
|
|
47
|
+
Python and Qt versions, libmpv availability, hardware-decode support,
|
|
48
|
+
and disk speed — it answers most of the follow-up questions at once.
|
|
49
|
+
render: text
|
|
50
|
+
|
|
51
|
+
- type: textarea
|
|
52
|
+
id: sources
|
|
53
|
+
attributes:
|
|
54
|
+
label: Sources loaded (timing issues only)
|
|
55
|
+
description: >
|
|
56
|
+
For a synchronisation or timing problem: which sources were loaded, what
|
|
57
|
+
their offsets and drift were, and whether any accepted TTL alignment was
|
|
58
|
+
in effect. This is usually the whole answer.
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest a capability
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
AvialSync is a visual inspection tool. Acquisition and built-in
|
|
9
|
+
scientific analysis are deliberately out of scope — those belong in
|
|
10
|
+
plugins. See BLUEPRINT.md for the scope boundary.
|
|
11
|
+
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: problem
|
|
14
|
+
attributes:
|
|
15
|
+
label: What are you trying to do
|
|
16
|
+
description: >
|
|
17
|
+
Describe the research task, not the feature. "I need to check whether
|
|
18
|
+
the encoder and the cameras drifted apart over an hour" tells us more
|
|
19
|
+
than "add a drift graph".
|
|
20
|
+
validations:
|
|
21
|
+
required: true
|
|
22
|
+
|
|
23
|
+
- type: textarea
|
|
24
|
+
id: workaround
|
|
25
|
+
attributes:
|
|
26
|
+
label: How do you do it today
|
|
27
|
+
description: What you currently do instead, and why that is unsatisfying.
|
|
28
|
+
|
|
29
|
+
- type: dropdown
|
|
30
|
+
id: layer
|
|
31
|
+
attributes:
|
|
32
|
+
label: Does this belong in core or in a plugin?
|
|
33
|
+
description: Lab-specific formats and event semantics are plugin territory.
|
|
34
|
+
options:
|
|
35
|
+
- Not sure
|
|
36
|
+
- Core — every user needs it
|
|
37
|
+
- Plugin — specific to my lab's formats or workflow
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
## What and why
|
|
2
|
+
|
|
3
|
+
<!-- What changed, and the problem it solves. Link the issue if there is one. -->
|
|
4
|
+
|
|
5
|
+
## How it was verified
|
|
6
|
+
|
|
7
|
+
<!-- The actual commands and their results, not "tests pass". If you fixed a
|
|
8
|
+
bug, say how you reproduced it first. -->
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
conda run -n avialsync ruff check .
|
|
12
|
+
conda run -n avialsync mypy src/avialsync/core
|
|
13
|
+
conda run -n avialsync mypy src/avialsync
|
|
14
|
+
QT_QPA_PLATFORM=offscreen conda run -n avialsync pytest -q --ignore=tests/benchmarks
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Checklist
|
|
18
|
+
|
|
19
|
+
- [ ] Tests ship with the code (`core/` logic → unit tests; UI behaviour →
|
|
20
|
+
pytest-qt; performance-relevant → benchmark)
|
|
21
|
+
- [ ] All four gate commands pass locally
|
|
22
|
+
- [ ] No test was weakened, skipped, or `# type: ignore`d to make this land
|
|
23
|
+
- [ ] `tests/test_sync_golden.py` untouched and passing (required for any change
|
|
24
|
+
to `core/timeline.py`, the playback loop, or seek logic)
|
|
25
|
+
- [ ] No performance budget regressed by more than 20 % on touched benchmarks
|
|
26
|
+
- [ ] `HANDOUT.md` updated if this changes a module's public API, adds a trap,
|
|
27
|
+
or fixes a listed bug
|
|
28
|
+
- [ ] `DECISIONS.md` entry added if this makes a choice future contributors
|
|
29
|
+
must not silently reverse
|
|
30
|
+
- [ ] New dependency? Licence named here, and it is not GPL/AGPL
|
|
31
|
+
|
|
32
|
+
## Anything reviewers should look at closely
|
|
33
|
+
|
|
34
|
+
<!-- Trade-offs you made, things you were unsure about, or areas where you would
|
|
35
|
+
like a second opinion. Saying "I am not certain about X" is welcome. -->
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Correctness on every push, on every branch: lint, types, and the full
|
|
2
|
+
# OS x Python test matrix. Nothing here freezes a bundle or builds an
|
|
3
|
+
# installer — that work belongs to a release tag and lives in release.yml,
|
|
4
|
+
# which additionally refuses any tag not reachable from main.
|
|
5
|
+
name: CI
|
|
6
|
+
|
|
7
|
+
on:
|
|
8
|
+
push:
|
|
9
|
+
# Every branch, not just main. Work sat on a feature branch for weeks with
|
|
10
|
+
# no CI at all, so the first signal would have arrived at merge time.
|
|
11
|
+
branches: [ "**" ]
|
|
12
|
+
pull_request:
|
|
13
|
+
branches: [ "main" ]
|
|
14
|
+
|
|
15
|
+
concurrency:
|
|
16
|
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
17
|
+
cancel-in-progress: true
|
|
18
|
+
|
|
19
|
+
permissions:
|
|
20
|
+
contents: read
|
|
21
|
+
|
|
22
|
+
env:
|
|
23
|
+
PYTHONUTF8: 1
|
|
24
|
+
QT_QPA_PLATFORM: offscreen
|
|
25
|
+
|
|
26
|
+
jobs:
|
|
27
|
+
lint-type:
|
|
28
|
+
name: Lint & Type Check
|
|
29
|
+
runs-on: ubuntu-24.04
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v5
|
|
32
|
+
- name: Set up Python
|
|
33
|
+
uses: actions/setup-python@v6
|
|
34
|
+
with:
|
|
35
|
+
python-version: "3.11"
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: pip install -e ".[dev]"
|
|
38
|
+
- name: Ruff Check
|
|
39
|
+
run: ruff check .
|
|
40
|
+
- name: Ruff Format Check
|
|
41
|
+
run: ruff format --check .
|
|
42
|
+
- name: Mypy Core
|
|
43
|
+
run: mypy src/avialsync/core
|
|
44
|
+
- name: Mypy All (standard mode; pre-existing errors suppressed in pyproject.toml)
|
|
45
|
+
run: mypy src/avialsync
|
|
46
|
+
|
|
47
|
+
test:
|
|
48
|
+
name: Test ${{ matrix.os }} - Python ${{ matrix.python-version }}
|
|
49
|
+
runs-on: ${{ matrix.os }}
|
|
50
|
+
needs: lint-type
|
|
51
|
+
timeout-minutes: 15
|
|
52
|
+
strategy:
|
|
53
|
+
fail-fast: false
|
|
54
|
+
matrix:
|
|
55
|
+
os: [ubuntu-24.04, macos-15, windows-2022]
|
|
56
|
+
python-version: ["3.11", "3.12"]
|
|
57
|
+
steps:
|
|
58
|
+
- uses: actions/checkout@v5
|
|
59
|
+
|
|
60
|
+
- name: Set up Python
|
|
61
|
+
uses: actions/setup-python@v6
|
|
62
|
+
with:
|
|
63
|
+
python-version: ${{ matrix.python-version }}
|
|
64
|
+
|
|
65
|
+
# --no-install-recommends is load-bearing, not tidiness: ffmpeg's
|
|
66
|
+
# recommends pull yt-dlp, pipewire and a 27 MB pocketsphinx speech model,
|
|
67
|
+
# none of which encode a fixture. A slow archive mirror spent twelve
|
|
68
|
+
# minutes on them and took the whole 15-minute job down with it. The
|
|
69
|
+
# codecs the fixtures need (libx264, libx265) are depends, not
|
|
70
|
+
# recommends, and the Make Fixtures step below proves they arrived.
|
|
71
|
+
- name: Install System Dependencies (Ubuntu)
|
|
72
|
+
if: runner.os == 'Linux'
|
|
73
|
+
run: sudo apt-get update && sudo apt-get install -y --no-install-recommends -o Acquire::Retries=3 ffmpeg libmpv2 libegl1
|
|
74
|
+
|
|
75
|
+
- name: Install System Dependencies (macOS)
|
|
76
|
+
if: runner.os == 'macOS'
|
|
77
|
+
run: brew install ffmpeg mpv
|
|
78
|
+
|
|
79
|
+
- name: Install System Dependencies (Windows)
|
|
80
|
+
if: runner.os == 'Windows'
|
|
81
|
+
shell: pwsh
|
|
82
|
+
run: |
|
|
83
|
+
choco install --no-progress ffmpeg
|
|
84
|
+
$archive = Join-Path $env:RUNNER_TEMP 'mpv-dev-x86_64-20260607-git-71ebd08.7z'
|
|
85
|
+
$destination = Join-Path $env:RUNNER_TEMP 'libmpv'
|
|
86
|
+
curl.exe --fail --location --silent --show-error --retry 5 --retry-all-errors --output $archive 'https://sourceforge.net/projects/mpv-player-windows/files/libmpv/mpv-dev-x86_64-20260607-git-71ebd08.7z/download'
|
|
87
|
+
if ((Get-FileHash -Algorithm SHA256 $archive).Hash -ne 'FAA0BE46643CD889A1D816696F60B9962D7BB70E9D9D6E619DA368D0B22211D6') {
|
|
88
|
+
throw 'Downloaded libmpv archive did not match its pinned SHA-256'
|
|
89
|
+
}
|
|
90
|
+
& "$env:ChocolateyInstall\tools\7z.exe" x $archive "-o$destination" -y | Out-Null
|
|
91
|
+
$libmpv = Get-ChildItem $destination -Filter 'libmpv-2.dll' -File -Recurse | Select-Object -First 1
|
|
92
|
+
if ($null -eq $libmpv) {
|
|
93
|
+
throw 'Pinned libmpv archive did not contain libmpv-2.dll'
|
|
94
|
+
}
|
|
95
|
+
$libmpv.DirectoryName | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
|
96
|
+
|
|
97
|
+
- name: Install Project Dependencies
|
|
98
|
+
run: pip install -e ".[dev]"
|
|
99
|
+
|
|
100
|
+
- name: Verify libmpv is importable (Windows)
|
|
101
|
+
if: runner.os == 'Windows'
|
|
102
|
+
run: python -c "import mpv; print('libmpv import succeeded')"
|
|
103
|
+
|
|
104
|
+
- name: Make Fixtures
|
|
105
|
+
run: python tools/make_fixtures.py --small
|
|
106
|
+
|
|
107
|
+
- name: Verify Determinism
|
|
108
|
+
run: |
|
|
109
|
+
cp -r tests/fixtures tests/fixtures_first_run
|
|
110
|
+
python tools/make_fixtures.py --small
|
|
111
|
+
python -c '
|
|
112
|
+
import pathlib, filecmp, sys
|
|
113
|
+
p1 = pathlib.Path("tests/fixtures")
|
|
114
|
+
p2 = pathlib.Path("tests/fixtures_first_run")
|
|
115
|
+
for f1 in p1.rglob("*.json"):
|
|
116
|
+
f2 = p2 / f1.relative_to(p1)
|
|
117
|
+
if not f2.exists() or not filecmp.cmp(f1, f2, shallow=False):
|
|
118
|
+
print(f"Mismatch in {f1}")
|
|
119
|
+
sys.exit(1)
|
|
120
|
+
print("Determinism check passed!")
|
|
121
|
+
'
|
|
122
|
+
|
|
123
|
+
- name: Run Tests
|
|
124
|
+
# A Qt/platform deadlock must fail at the specific test instead of
|
|
125
|
+
# consuming the whole job timeout with no diagnostic evidence.
|
|
126
|
+
run: pytest --maxfail=5 -q --durations=20 --timeout=60 --timeout-method=thread --ignore=tests/benchmarks
|
|
127
|
+
|
|
128
|
+
docs:
|
|
129
|
+
name: Read the Docs build
|
|
130
|
+
runs-on: ubuntu-24.04
|
|
131
|
+
needs: lint-type
|
|
132
|
+
steps:
|
|
133
|
+
- uses: actions/checkout@v5
|
|
134
|
+
- uses: actions/setup-python@v6
|
|
135
|
+
with:
|
|
136
|
+
python-version: "3.11"
|
|
137
|
+
- name: Install documentation dependencies
|
|
138
|
+
run: pip install -e ".[docs]"
|
|
139
|
+
- name: Build documentation with warnings as errors
|
|
140
|
+
run: sphinx-build -W --keep-going -b html docs docs/_build/html
|