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.
Files changed (269) hide show
  1. avialsync-0.1.0b5/.flake8 +21 -0
  2. avialsync-0.1.0b5/.gitattributes +5 -0
  3. avialsync-0.1.0b5/.githooks/post-commit +7 -0
  4. avialsync-0.1.0b5/.githooks/pre-commit +25 -0
  5. avialsync-0.1.0b5/.github/ISSUE_TEMPLATE/bug_report.yml +58 -0
  6. avialsync-0.1.0b5/.github/ISSUE_TEMPLATE/config.yml +5 -0
  7. avialsync-0.1.0b5/.github/ISSUE_TEMPLATE/feature_request.yml +37 -0
  8. avialsync-0.1.0b5/.github/pull_request_template.md +35 -0
  9. avialsync-0.1.0b5/.github/workflows/ci.yml +140 -0
  10. avialsync-0.1.0b5/.github/workflows/release.yml +410 -0
  11. avialsync-0.1.0b5/.gitignore +156 -0
  12. avialsync-0.1.0b5/.pre-commit-config.yaml +7 -0
  13. avialsync-0.1.0b5/.readthedocs.yaml +17 -0
  14. avialsync-0.1.0b5/AGENTS.md +227 -0
  15. avialsync-0.1.0b5/ARCHITECTURE.md +454 -0
  16. avialsync-0.1.0b5/BLUEPRINT.md +294 -0
  17. avialsync-0.1.0b5/CLAUDE.md +4 -0
  18. avialsync-0.1.0b5/CODE_OF_CONDUCT.md +129 -0
  19. avialsync-0.1.0b5/CONTRIBUTING.md +117 -0
  20. avialsync-0.1.0b5/DECISIONS.md +1732 -0
  21. avialsync-0.1.0b5/GEMINI.md +4 -0
  22. avialsync-0.1.0b5/HANDOUT.md +1005 -0
  23. avialsync-0.1.0b5/LICENSE +3 -0
  24. avialsync-0.1.0b5/PKG-INFO +236 -0
  25. avialsync-0.1.0b5/PLOT_UX_PLAN.md +312 -0
  26. avialsync-0.1.0b5/PROMPTS.md +171 -0
  27. avialsync-0.1.0b5/README.md +203 -0
  28. avialsync-0.1.0b5/RECOVERY_PLAN.md +429 -0
  29. avialsync-0.1.0b5/RECOVERY_PROMPT.md +593 -0
  30. avialsync-0.1.0b5/TESTING.md +201 -0
  31. avialsync-0.1.0b5/assets/avial_sync.png +0 -0
  32. avialsync-0.1.0b5/docs/_static/screenshots/demo_step1_empty.png +0 -0
  33. avialsync-0.1.0b5/docs/_static/screenshots/demo_step2_video_loaded.png +0 -0
  34. avialsync-0.1.0b5/docs/_static/screenshots/demo_step3_csv_loaded.png +0 -0
  35. avialsync-0.1.0b5/docs/_static/screenshots/demo_step4_wizard_open.png +0 -0
  36. avialsync-0.1.0b5/docs/_static/screenshots/demo_step5_wizard_configured.png +0 -0
  37. avialsync-0.1.0b5/docs/_static/screenshots/demo_step6_wizard_previewed.png +0 -0
  38. avialsync-0.1.0b5/docs/_static/screenshots/demo_step7_mapping_applied.png +0 -0
  39. avialsync-0.1.0b5/docs/conf.py +21 -0
  40. avialsync-0.1.0b5/docs/formats.md +23 -0
  41. avialsync-0.1.0b5/docs/index.rst +19 -0
  42. avialsync-0.1.0b5/docs/plugin-guide.md +51 -0
  43. avialsync-0.1.0b5/docs/quickstart.md +67 -0
  44. avialsync-0.1.0b5/docs/technical/architecture.md +72 -0
  45. avialsync-0.1.0b5/docs/technical/data-handling.md +103 -0
  46. avialsync-0.1.0b5/docs/technical/index.md +13 -0
  47. avialsync-0.1.0b5/docs/technical/performance.md +89 -0
  48. avialsync-0.1.0b5/docs/troubleshooting.md +31 -0
  49. avialsync-0.1.0b5/docs/tutorials/first-session.md +32 -0
  50. avialsync-0.1.0b5/docs/tutorials/synchronization.md +26 -0
  51. avialsync-0.1.0b5/docs/user-guide/index.md +54 -0
  52. avialsync-0.1.0b5/examples/data/camera_1.mp4 +0 -0
  53. avialsync-0.1.0b5/examples/data/camera_2.mp4 +0 -0
  54. avialsync-0.1.0b5/examples/data/camera_3.mp4 +0 -0
  55. avialsync-0.1.0b5/examples/data/camera_vfr.mp4 +0 -0
  56. avialsync-0.1.0b5/examples/data/mouse_wheel_run_view0.mp4 +0 -0
  57. avialsync-0.1.0b5/examples/data/mouse_wheel_run_view1.mp4 +0 -0
  58. avialsync-0.1.0b5/examples/data/mouse_wheel_run_view2.mp4 +0 -0
  59. avialsync-0.1.0b5/examples/data/openephys_mock/experiment1/recording1/continuous/Acquisition_Board-100.Rhythm_Data/continuous.dat +0 -0
  60. avialsync-0.1.0b5/examples/data/openephys_mock/experiment1/recording1/continuous/Acquisition_Board-100.Rhythm_Data/timestamps.npy +0 -0
  61. avialsync-0.1.0b5/examples/data/openephys_mock/experiment1/recording1/structure.oebin +50 -0
  62. avialsync-0.1.0b5/examples/data/pose.csv +303 -0
  63. avialsync-0.1.0b5/examples/data/sensors.csv +10001 -0
  64. avialsync-0.1.0b5/examples/data/sensors_gaps.csv +98501 -0
  65. avialsync-0.1.0b5/examples/data/tracking_dlc.csv +303 -0
  66. avialsync-0.1.0b5/examples/load_tenss_data.py +58 -0
  67. avialsync-0.1.0b5/examples/plugins/__init__.py +1 -0
  68. avialsync-0.1.0b5/examples/plugins/avialsync-plugin-example/pyproject.toml +16 -0
  69. avialsync-0.1.0b5/examples/plugins/avialsync-plugin-example/src/avialsync_plugin_example/__init__.py +48 -0
  70. avialsync-0.1.0b5/examples/profile_loading.py +27 -0
  71. avialsync-0.1.0b5/graphify-out/.gitignore +8 -0
  72. avialsync-0.1.0b5/graphify-out/GRAPH_REPORT.md +183 -0
  73. avialsync-0.1.0b5/graphify-out/README.md +12 -0
  74. avialsync-0.1.0b5/graphify-out/graph.html +39 -0
  75. avialsync-0.1.0b5/graphify-out/graph.json +132697 -0
  76. avialsync-0.1.0b5/packaging/avialsync.spec +98 -0
  77. avialsync-0.1.0b5/packaging/build_bundle.py +43 -0
  78. avialsync-0.1.0b5/packaging/conda/meta.yaml +71 -0
  79. avialsync-0.1.0b5/packaging/fetch_media_libs.py +138 -0
  80. avialsync-0.1.0b5/packaging/linux/avialsync.png +0 -0
  81. avialsync-0.1.0b5/packaging/linux/make_appimage.sh +31 -0
  82. avialsync-0.1.0b5/packaging/macos/avialsync.icns +0 -0
  83. avialsync-0.1.0b5/packaging/macos/make_dmg.sh +16 -0
  84. avialsync-0.1.0b5/packaging/macos/sign_notarize.sh +82 -0
  85. avialsync-0.1.0b5/packaging/probe_dialog_test.py +2 -0
  86. avialsync-0.1.0b5/packaging/smoke_test.py +99 -0
  87. avialsync-0.1.0b5/packaging/windows/avialsync.ico +0 -0
  88. avialsync-0.1.0b5/packaging/windows/avialsync.iss +33 -0
  89. avialsync-0.1.0b5/packaging/windows/sign.ps1 +60 -0
  90. avialsync-0.1.0b5/pyproject.toml +124 -0
  91. avialsync-0.1.0b5/src/avialsync/__init__.py +3 -0
  92. avialsync-0.1.0b5/src/avialsync/__main__.py +117 -0
  93. avialsync-0.1.0b5/src/avialsync/core/__init__.py +1 -0
  94. avialsync-0.1.0b5/src/avialsync/core/cache.py +183 -0
  95. avialsync-0.1.0b5/src/avialsync/core/channel_reader.py +165 -0
  96. avialsync-0.1.0b5/src/avialsync/core/errors.py +73 -0
  97. avialsync-0.1.0b5/src/avialsync/core/inspection.py +146 -0
  98. avialsync-0.1.0b5/src/avialsync/core/pyramid.py +508 -0
  99. avialsync-0.1.0b5/src/avialsync/core/registry.py +150 -0
  100. avialsync-0.1.0b5/src/avialsync/core/session.py +257 -0
  101. avialsync-0.1.0b5/src/avialsync/core/source.py +173 -0
  102. avialsync-0.1.0b5/src/avialsync/core/sync.py +374 -0
  103. avialsync-0.1.0b5/src/avialsync/core/timeline.py +299 -0
  104. avialsync-0.1.0b5/src/avialsync/demo.py +531 -0
  105. avialsync-0.1.0b5/src/avialsync/engine/__init__.py +1 -0
  106. avialsync-0.1.0b5/src/avialsync/engine/drop_worker.py +279 -0
  107. avialsync-0.1.0b5/src/avialsync/engine/export.py +227 -0
  108. avialsync-0.1.0b5/src/avialsync/engine/export_worker.py +200 -0
  109. avialsync-0.1.0b5/src/avialsync/engine/importer.py +312 -0
  110. avialsync-0.1.0b5/src/avialsync/engine/player.py +433 -0
  111. avialsync-0.1.0b5/src/avialsync/engine/proxy.py +106 -0
  112. avialsync-0.1.0b5/src/avialsync/engine/seeker.py +40 -0
  113. avialsync-0.1.0b5/src/avialsync/engine/session_worker.py +70 -0
  114. avialsync-0.1.0b5/src/avialsync/engine/sync_worker.py +96 -0
  115. avialsync-0.1.0b5/src/avialsync/engine/video_worker.py +65 -0
  116. avialsync-0.1.0b5/src/avialsync/loaders/__init__.py +1 -0
  117. avialsync-0.1.0b5/src/avialsync/loaders/aol_eks_loader.py +259 -0
  118. avialsync-0.1.0b5/src/avialsync/loaders/aol_encoder_loader.py +287 -0
  119. avialsync-0.1.0b5/src/avialsync/loaders/aol_session_loader.py +356 -0
  120. avialsync-0.1.0b5/src/avialsync/loaders/csv_loader.py +339 -0
  121. avialsync-0.1.0b5/src/avialsync/loaders/neo_loader.py +328 -0
  122. avialsync-0.1.0b5/src/avialsync/loaders/tracking_loader.py +152 -0
  123. avialsync-0.1.0b5/src/avialsync/loaders/video_standard.py +276 -0
  124. avialsync-0.1.0b5/src/avialsync/resources/__init__.py +1 -0
  125. avialsync-0.1.0b5/src/avialsync/resources/avialsync.png +0 -0
  126. avialsync-0.1.0b5/src/avialsync/runtime.py +161 -0
  127. avialsync-0.1.0b5/src/avialsync/ui/__init__.py +1 -0
  128. avialsync-0.1.0b5/src/avialsync/ui/annotations.py +211 -0
  129. avialsync-0.1.0b5/src/avialsync/ui/batch_import_dialog.py +130 -0
  130. avialsync-0.1.0b5/src/avialsync/ui/diagnostics.py +213 -0
  131. avialsync-0.1.0b5/src/avialsync/ui/import_report.py +105 -0
  132. avialsync-0.1.0b5/src/avialsync/ui/import_wizard.py +397 -0
  133. avialsync-0.1.0b5/src/avialsync/ui/job_manager.py +297 -0
  134. avialsync-0.1.0b5/src/avialsync/ui/main_window.py +2884 -0
  135. avialsync-0.1.0b5/src/avialsync/ui/offsets_panel.py +1 -0
  136. avialsync-0.1.0b5/src/avialsync/ui/pane_proportions.py +199 -0
  137. avialsync-0.1.0b5/src/avialsync/ui/plot_header.py +69 -0
  138. avialsync-0.1.0b5/src/avialsync/ui/plot_interactions.py +142 -0
  139. avialsync-0.1.0b5/src/avialsync/ui/plot_overlays.py +177 -0
  140. avialsync-0.1.0b5/src/avialsync/ui/plot_pane.py +812 -0
  141. avialsync-0.1.0b5/src/avialsync/ui/plot_row.py +274 -0
  142. avialsync-0.1.0b5/src/avialsync/ui/plot_sweep.py +342 -0
  143. avialsync-0.1.0b5/src/avialsync/ui/readout_panel.py +335 -0
  144. avialsync-0.1.0b5/src/avialsync/ui/recent_files.py +40 -0
  145. avialsync-0.1.0b5/src/avialsync/ui/relink_dialog.py +118 -0
  146. avialsync-0.1.0b5/src/avialsync/ui/shortcuts_dialog.py +92 -0
  147. avialsync-0.1.0b5/src/avialsync/ui/sidebar.py +535 -0
  148. avialsync-0.1.0b5/src/avialsync/ui/source_properties.py +237 -0
  149. avialsync-0.1.0b5/src/avialsync/ui/sync_wizard.py +219 -0
  150. avialsync-0.1.0b5/src/avialsync/ui/theme.py +365 -0
  151. avialsync-0.1.0b5/src/avialsync/ui/time_format.py +43 -0
  152. avialsync-0.1.0b5/src/avialsync/ui/tracking_3d_pane.py +648 -0
  153. avialsync-0.1.0b5/src/avialsync/ui/tracking_colors.py +111 -0
  154. avialsync-0.1.0b5/src/avialsync/ui/transport.py +948 -0
  155. avialsync-0.1.0b5/src/avialsync/ui/ui_heartbeat.py +83 -0
  156. avialsync-0.1.0b5/src/avialsync/ui/video_grid.py +353 -0
  157. avialsync-0.1.0b5/src/avialsync/ui/video_overlay.py +272 -0
  158. avialsync-0.1.0b5/src/avialsync/ui/video_pane.py +608 -0
  159. avialsync-0.1.0b5/src/avialsync/ui/video_timing.py +282 -0
  160. avialsync-0.1.0b5/tests/benchmarks/test_bench_plot_pane.py +167 -0
  161. avialsync-0.1.0b5/tests/benchmarks/test_bench_pyramid.py +218 -0
  162. avialsync-0.1.0b5/tests/benchmarks/test_bench_sync.py +30 -0
  163. avialsync-0.1.0b5/tests/benchmarks/test_bench_tracking_3d.py +59 -0
  164. avialsync-0.1.0b5/tests/benchmarks/test_bench_video_timing.py +67 -0
  165. avialsync-0.1.0b5/tests/conftest.py +1 -0
  166. avialsync-0.1.0b5/tests/fixtures/session_v1.avv +29 -0
  167. avialsync-0.1.0b5/tests/fixtures/session_v2.avv +28 -0
  168. avialsync-0.1.0b5/tests/fixtures/session_v3.avv +44 -0
  169. avialsync-0.1.0b5/tests/test_annotation_frames.py +224 -0
  170. avialsync-0.1.0b5/tests/test_aol_chunk_boundaries.py +168 -0
  171. avialsync-0.1.0b5/tests/test_aol_loaders.py +555 -0
  172. avialsync-0.1.0b5/tests/test_aol_pose_routing.py +290 -0
  173. avialsync-0.1.0b5/tests/test_axis.py +84 -0
  174. avialsync-0.1.0b5/tests/test_channel_identity.py +180 -0
  175. avialsync-0.1.0b5/tests/test_ci_platform_config.py +185 -0
  176. avialsync-0.1.0b5/tests/test_cli_demo.py +219 -0
  177. avialsync-0.1.0b5/tests/test_cli_open.py +111 -0
  178. avialsync-0.1.0b5/tests/test_close_and_focus.py +351 -0
  179. avialsync-0.1.0b5/tests/test_conda_recipe.py +69 -0
  180. avialsync-0.1.0b5/tests/test_core_cache.py +163 -0
  181. avialsync-0.1.0b5/tests/test_core_coverage_edges.py +289 -0
  182. avialsync-0.1.0b5/tests/test_core_edges.py +114 -0
  183. avialsync-0.1.0b5/tests/test_core_inspection.py +126 -0
  184. avialsync-0.1.0b5/tests/test_core_pyramid.py +239 -0
  185. avialsync-0.1.0b5/tests/test_core_registry.py +94 -0
  186. avialsync-0.1.0b5/tests/test_core_remaining_paths.py +448 -0
  187. avialsync-0.1.0b5/tests/test_core_session.py +244 -0
  188. avialsync-0.1.0b5/tests/test_core_sync.py +76 -0
  189. avialsync-0.1.0b5/tests/test_core_sync_guards.py +148 -0
  190. avialsync-0.1.0b5/tests/test_core_timeline.py +268 -0
  191. avialsync-0.1.0b5/tests/test_demo_data.py +68 -0
  192. avialsync-0.1.0b5/tests/test_diagnostics.py +60 -0
  193. avialsync-0.1.0b5/tests/test_engine_export.py +102 -0
  194. avialsync-0.1.0b5/tests/test_engine_importer.py +108 -0
  195. avialsync-0.1.0b5/tests/test_engine_layering.py +83 -0
  196. avialsync-0.1.0b5/tests/test_engine_sync_worker.py +34 -0
  197. avialsync-0.1.0b5/tests/test_exact_logic.py +81 -0
  198. avialsync-0.1.0b5/tests/test_fixtures_protected.py +53 -0
  199. avialsync-0.1.0b5/tests/test_frame_indexed.py +181 -0
  200. avialsync-0.1.0b5/tests/test_headless_core.py +95 -0
  201. avialsync-0.1.0b5/tests/test_hot_path.py +183 -0
  202. avialsync-0.1.0b5/tests/test_import_streaming.py +267 -0
  203. avialsync-0.1.0b5/tests/test_interaction_standard.py +359 -0
  204. avialsync-0.1.0b5/tests/test_loaders_csv.py +250 -0
  205. avialsync-0.1.0b5/tests/test_loaders_neo.py +112 -0
  206. avialsync-0.1.0b5/tests/test_loaders_tracking.py +75 -0
  207. avialsync-0.1.0b5/tests/test_loaders_video.py +162 -0
  208. avialsync-0.1.0b5/tests/test_never_freeze.py +297 -0
  209. avialsync-0.1.0b5/tests/test_packaging_media.py +252 -0
  210. avialsync-0.1.0b5/tests/test_packaging_metadata.py +14 -0
  211. avialsync-0.1.0b5/tests/test_packaging_smoke.py +149 -0
  212. avialsync-0.1.0b5/tests/test_packaging_spec.py +21 -0
  213. avialsync-0.1.0b5/tests/test_pane_proportions.py +209 -0
  214. avialsync-0.1.0b5/tests/test_playback_smoothness.py +462 -0
  215. avialsync-0.1.0b5/tests/test_plugin_discovery.py +135 -0
  216. avialsync-0.1.0b5/tests/test_prepare_release.py +42 -0
  217. avialsync-0.1.0b5/tests/test_pyramid_hierarchy.py +27 -0
  218. avialsync-0.1.0b5/tests/test_pyramid_read_api.py +181 -0
  219. avialsync-0.1.0b5/tests/test_runtime.py +41 -0
  220. avialsync-0.1.0b5/tests/test_scientific_session_workload.py +38 -0
  221. avialsync-0.1.0b5/tests/test_scrubbing.py +312 -0
  222. avialsync-0.1.0b5/tests/test_seeker.py +52 -0
  223. avialsync-0.1.0b5/tests/test_session_worker.py +264 -0
  224. avialsync-0.1.0b5/tests/test_source_time_map.py +169 -0
  225. avialsync-0.1.0b5/tests/test_subprocess_no_window.py +108 -0
  226. avialsync-0.1.0b5/tests/test_sync_golden.py +213 -0
  227. avialsync-0.1.0b5/tests/test_theme_tooltips.py +286 -0
  228. avialsync-0.1.0b5/tests/test_tracking_colors.py +98 -0
  229. avialsync-0.1.0b5/tests/test_transport_layout.py +236 -0
  230. avialsync-0.1.0b5/tests/test_transport_resize.py +116 -0
  231. avialsync-0.1.0b5/tests/test_typed_source_errors.py +155 -0
  232. avialsync-0.1.0b5/tests/test_ui_dialogs.py +278 -0
  233. avialsync-0.1.0b5/tests/test_ui_exact_sync.out +0 -0
  234. avialsync-0.1.0b5/tests/test_ui_exact_sync.py +70 -0
  235. avialsync-0.1.0b5/tests/test_ui_follow.py +297 -0
  236. avialsync-0.1.0b5/tests/test_ui_layout_resize.py +205 -0
  237. avialsync-0.1.0b5/tests/test_ui_main.py +583 -0
  238. avialsync-0.1.0b5/tests/test_ui_plot_gutter.py +83 -0
  239. avialsync-0.1.0b5/tests/test_ui_plot_measure.py +81 -0
  240. avialsync-0.1.0b5/tests/test_ui_plot_sliced_refresh.py +102 -0
  241. avialsync-0.1.0b5/tests/test_ui_readout_delta.py +74 -0
  242. avialsync-0.1.0b5/tests/test_ui_sensor_mapping.py +152 -0
  243. avialsync-0.1.0b5/tests/test_ui_source_properties.py +157 -0
  244. avialsync-0.1.0b5/tests/test_ui_sync_wizard.py +42 -0
  245. avialsync-0.1.0b5/tests/test_ui_time_format.py +82 -0
  246. avialsync-0.1.0b5/tests/test_ui_tracking_3d.py +187 -0
  247. avialsync-0.1.0b5/tests/test_video_grid.py +70 -0
  248. avialsync-0.1.0b5/tests/test_video_pane.py +118 -0
  249. avialsync-0.1.0b5/tests/test_video_pane_timing.py +268 -0
  250. avialsync-0.1.0b5/tests/test_video_worker.py +59 -0
  251. avialsync-0.1.0b5/tests/test_worker_lifetime.py +121 -0
  252. avialsync-0.1.0b5/tests/test_worker_thread_teardown.py +42 -0
  253. avialsync-0.1.0b5/tests/test_workload_responsiveness.py +141 -0
  254. avialsync-0.1.0b5/tests/util_framestrip.py +148 -0
  255. avialsync-0.1.0b5/tools/check_ephys_channels.py +18 -0
  256. avialsync-0.1.0b5/tools/check_video_fps.py +12 -0
  257. avialsync-0.1.0b5/tools/generate_demo_data.py +30 -0
  258. avialsync-0.1.0b5/tools/generate_demo_screenshots.py +133 -0
  259. avialsync-0.1.0b5/tools/generate_icons.py +72 -0
  260. avialsync-0.1.0b5/tools/generate_tenss_screenshots.py +109 -0
  261. avialsync-0.1.0b5/tools/launch_demo.py +9 -0
  262. avialsync-0.1.0b5/tools/make_fixtures.py +589 -0
  263. avialsync-0.1.0b5/tools/prepare_release.py +160 -0
  264. avialsync-0.1.0b5/tools/test_edges.py +15 -0
  265. avialsync-0.1.0b5/tools/test_fit.py +32 -0
  266. avialsync-0.1.0b5/tools/test_layout.py +25 -0
  267. avialsync-0.1.0b5/tools/test_neo_events.py +18 -0
  268. avialsync-0.1.0b5/tools/test_sync_tenss.py +89 -0
  269. 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,5 @@
1
+ # Generated developer visualisation; retain it in Git without affecting language statistics.
2
+ graphify-out/graph.html linguist-generated
3
+
4
+ # Shell packaging helpers are executed by Bash in Windows checkout tests too.
5
+ *.sh text eol=lf
@@ -0,0 +1,7 @@
1
+ #!/bin/sh
2
+ # Local developer convenience only. It never stages, commits, pushes, or runs in CI.
3
+ if command -v graphify >/dev/null 2>&1; then
4
+ graphify update . --no-cluster
5
+ else
6
+ printf '%s\n' 'graphify is not installed; graph was not refreshed.' >&2
7
+ fi
@@ -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,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Question or usage help
4
+ url: https://github.com/anzalks/avialsync/discussions
5
+ about: For "how do I…" questions, please use Discussions rather than an issue.
@@ -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