TwoPy 0.1.9__tar.gz → 0.2.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.
Files changed (223) hide show
  1. {twopy-0.1.9 → twopy-0.2.0}/PKG-INFO +2 -1
  2. {twopy-0.1.9 → twopy-0.2.0}/README.md +1 -0
  3. {twopy-0.1.9 → twopy-0.2.0}/pyproject.toml +1 -1
  4. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/__init__.py +40 -9
  5. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/__init__.py +38 -10
  6. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/group_matching.py +149 -0
  7. twopy-0.2.0/src/twopy/analysis/kernels/__init__.py +352 -0
  8. twopy-0.2.0/src/twopy/analysis/kernels/_fitting.py +360 -0
  9. twopy-0.2.0/src/twopy/analysis/kernels/_stimulus.py +432 -0
  10. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/persistence.py +9 -18
  11. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_context.py +8 -11
  12. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_maps.py +9 -7
  13. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_processing/__init__.py +1 -1
  14. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_processing/options.py +7 -8
  15. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_processing/signals.py +7 -4
  16. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_window_options.py +1 -1
  17. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/responses.py +63 -19
  18. twopy-0.2.0/src/twopy/analysis/timing.py +315 -0
  19. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/trials.py +1 -84
  20. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/workflow.py +9 -9
  21. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis_cache.py +23 -4
  22. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/api.py +6 -4
  23. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/config.py +172 -36
  24. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/conversion/api.py +63 -6
  25. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/conversion/hdf5_writing.py +1 -1
  26. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/conversion/source_loading.py +26 -6
  27. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/conversion/stimulus_code.py +1 -1
  28. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/conversion/types.py +2 -2
  29. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/converted.py +60 -1
  30. twopy-0.2.0/src/twopy/custom/__init__.py +69 -0
  31. twopy-0.2.0/src/twopy/custom/discovery.py +303 -0
  32. twopy-0.2.0/src/twopy/custom/native.py +27 -0
  33. twopy-0.2.0/src/twopy/custom/native_workflows/__init__.py +5 -0
  34. twopy-0.2.0/src/twopy/custom/native_workflows/direction_selectivity.py +201 -0
  35. twopy-0.2.0/src/twopy/custom/native_workflows/response_kernels.py +502 -0
  36. twopy-0.2.0/src/twopy/custom/parameters.py +380 -0
  37. twopy-0.2.0/src/twopy/custom/provenance.py +385 -0
  38. twopy-0.2.0/src/twopy/custom/registry.py +78 -0
  39. twopy-0.2.0/src/twopy/custom/types.py +838 -0
  40. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/__init__.py +4 -2
  41. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/cache.py +1 -1
  42. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/catalog.py +1 -1
  43. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/generic.py +1 -1
  44. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/modeled.py +7 -4
  45. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/search.py +146 -5
  46. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/types.py +0 -3
  47. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/hdf5_utils.py +2 -2
  48. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/controls.py +84 -268
  49. twopy-0.2.0/src/twopy/napari/custom_tab.py +607 -0
  50. twopy-0.2.0/src/twopy/napari/database_favorites.py +416 -0
  51. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/database_search.py +346 -67
  52. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/display_paths.py +15 -3
  53. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/group_matching.py +12 -1
  54. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/group_matching_roi.py +168 -104
  55. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/interactive.py +17 -21
  56. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/live_analysis.py +58 -98
  57. twopy-0.2.0/src/twopy/napari/load_workflow.py +409 -0
  58. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/loading.py +33 -13
  59. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/data.py +89 -47
  60. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/api.py +2 -2
  61. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/options_panel.py +6 -0
  62. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/plot_area.py +46 -0
  63. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/response_map_area.py +13 -0
  64. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/response_plot_widget.py +561 -15
  65. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/save_actions.py +30 -67
  66. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/export.py +37 -0
  67. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/export_controls.py +10 -1
  68. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/form_controls.py +5 -1
  69. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/normalization_options.py +6 -1
  70. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/processing_options.py +171 -94
  71. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/roi_generation/actions.py +2 -2
  72. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/widgets.py +67 -0
  73. twopy-0.2.0/src/twopy/napari/responses.py +245 -0
  74. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/session.py +22 -0
  75. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/sidebar.py +9 -0
  76. twopy-0.2.0/src/twopy/napari/text.py +64 -0
  77. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/trial_timeline.py +3 -3
  78. {twopy-0.1.9 → twopy-0.2.0}/src/twopy.egg-info/PKG-INFO +2 -1
  79. {twopy-0.1.9 → twopy-0.2.0}/src/twopy.egg-info/SOURCES.txt +38 -0
  80. {twopy-0.1.9 → twopy-0.2.0}/tests/test_api.py +12 -2
  81. {twopy-0.1.9 → twopy-0.2.0}/tests/test_background_subtraction.py +2 -4
  82. {twopy-0.1.9 → twopy-0.2.0}/tests/test_config.py +148 -19
  83. {twopy-0.1.9 → twopy-0.2.0}/tests/test_conversion.py +181 -2
  84. {twopy-0.1.9 → twopy-0.2.0}/tests/test_converted.py +63 -101
  85. twopy-0.2.0/tests/test_custom_workflows.py +926 -0
  86. {twopy-0.1.9 → twopy-0.2.0}/tests/test_database.py +40 -3
  87. {twopy-0.1.9 → twopy-0.2.0}/tests/test_epoch_mapping.py +5 -30
  88. twopy-0.2.0/tests/test_group_matching.py +384 -0
  89. {twopy-0.1.9 → twopy-0.2.0}/tests/test_hdf5_utils.py +1 -2
  90. twopy-0.2.0/tests/test_kernels.py +482 -0
  91. twopy-0.2.0/tests/test_load_workflow.py +617 -0
  92. {twopy-0.1.9 → twopy-0.2.0}/tests/test_motion.py +4 -33
  93. twopy-0.2.0/tests/test_napari.py +505 -0
  94. twopy-0.2.0/tests/test_napari_database_search.py +639 -0
  95. twopy-0.2.0/tests/test_napari_display.py +132 -0
  96. twopy-0.2.0/tests/test_napari_export.py +324 -0
  97. twopy-0.2.0/tests/test_napari_live_controller.py +574 -0
  98. twopy-0.2.0/tests/test_napari_load_tab.py +445 -0
  99. twopy-0.2.0/tests/test_napari_loaded_recordings.py +626 -0
  100. twopy-0.2.0/tests/test_napari_path_resolution.py +619 -0
  101. twopy-0.2.0/tests/test_napari_plot_controls.py +466 -0
  102. twopy-0.2.0/tests/test_napari_plot_data.py +366 -0
  103. twopy-0.2.0/tests/test_napari_plot_widgets.py +799 -0
  104. twopy-0.2.0/tests/test_napari_processing_controls.py +650 -0
  105. twopy-0.2.0/tests/test_napari_response_map_display.py +85 -0
  106. twopy-0.2.0/tests/test_napari_response_workflow.py +756 -0
  107. twopy-0.2.0/tests/test_napari_roi_generation.py +620 -0
  108. twopy-0.2.0/tests/test_napari_roi_visibility.py +314 -0
  109. twopy-0.2.0/tests/test_napari_text.py +49 -0
  110. twopy-0.2.0/tests/test_napari_timeline.py +251 -0
  111. {twopy-0.1.9 → twopy-0.2.0}/tests/test_packaging.py +15 -23
  112. {twopy-0.1.9 → twopy-0.2.0}/tests/test_parity.py +9 -31
  113. {twopy-0.1.9 → twopy-0.2.0}/tests/test_parity_psycho5.py +7 -62
  114. {twopy-0.1.9 → twopy-0.2.0}/tests/test_photodiode_classification.py +4 -31
  115. twopy-0.2.0/tests/test_recording_timing.py +200 -0
  116. {twopy-0.1.9 → twopy-0.2.0}/tests/test_response_maps.py +6 -32
  117. {twopy-0.1.9 → twopy-0.2.0}/tests/test_response_processing.py +45 -28
  118. {twopy-0.1.9 → twopy-0.2.0}/tests/test_response_roi_extraction.py +7 -33
  119. {twopy-0.1.9 → twopy-0.2.0}/tests/test_responses.py +21 -0
  120. {twopy-0.1.9 → twopy-0.2.0}/tests/test_roi.py +2 -6
  121. {twopy-0.1.9 → twopy-0.2.0}/tests/test_stimulus.py +38 -73
  122. {twopy-0.1.9 → twopy-0.2.0}/tests/test_synchronization.py +25 -57
  123. twopy-0.2.0/tests/test_trials.py +182 -0
  124. {twopy-0.1.9 → twopy-0.2.0}/tests/test_workflow.py +9 -72
  125. twopy-0.1.9/src/twopy/napari/responses.py +0 -141
  126. twopy-0.1.9/src/twopy/napari/text.py +0 -26
  127. twopy-0.1.9/tests/test_group_matching.py +0 -180
  128. twopy-0.1.9/tests/test_napari.py +0 -7001
  129. twopy-0.1.9/tests/test_trials.py +0 -372
  130. {twopy-0.1.9 → twopy-0.2.0}/setup.cfg +0 -0
  131. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/_segments.py +0 -0
  132. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/_version.py +0 -0
  133. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/background_subtraction.py +0 -0
  134. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/dff.py +0 -0
  135. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/dff_options.py +0 -0
  136. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/epoch_mapping.py +0 -0
  137. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/motion.py +0 -0
  138. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_processing/apply.py +0 -0
  139. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_processing/persistence.py +0 -0
  140. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/analysis/response_processing/qc.py +0 -0
  141. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/calibrations/pixel_calibration_profiles.csv +0 -0
  142. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/calibrations/pixel_size.csv +0 -0
  143. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/conversion/__init__.py +0 -0
  144. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/conversion/alignment_crop.py +0 -0
  145. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/conversion/matlab_values.py +0 -0
  146. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/connection.py +0 -0
  147. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/database/sql.py +0 -0
  148. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/filenames.py +0 -0
  149. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/frame_ranges.py +0 -0
  150. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/inspection.py +0 -0
  151. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/matlab.py +0 -0
  152. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/__init__.py +0 -0
  153. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/__main__.py +0 -0
  154. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/dims.py +0 -0
  155. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/display.py +0 -0
  156. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/errors.py +0 -0
  157. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/group_matching_images.py +0 -0
  158. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/latest_worker.py +0 -0
  159. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/launcher.py +0 -0
  160. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/loaded_recordings_csv.py +0 -0
  161. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/movie.py +0 -0
  162. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/paths.py +0 -0
  163. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/__init__.py +0 -0
  164. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/dff_options.py +0 -0
  165. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/__init__.py +0 -0
  166. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/dynamic_options.py +0 -0
  167. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/paths.py +0 -0
  168. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/docks/visibility_state.py +0 -0
  169. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/label_visibility.py +0 -0
  170. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/options.py +0 -0
  171. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/panels.py +0 -0
  172. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/response_map_colors.py +0 -0
  173. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/response_map_display.py +0 -0
  174. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/response_map_options.py +0 -0
  175. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/response_window_options.py +0 -0
  176. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/roi_generation/__init__.py +0 -0
  177. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/roi_generation/controls.py +0 -0
  178. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/plotting/roi_generation/options.py +0 -0
  179. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/protocols.py +0 -0
  180. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/roi.py +0 -0
  181. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/state.py +0 -0
  182. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/types.py +0 -0
  183. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/napari/viewer.py +0 -0
  184. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/parity/__init__.py +0 -0
  185. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/parity/dff.py +0 -0
  186. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/parity/psycho5.py +0 -0
  187. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/parity/roi.py +0 -0
  188. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/parity/roi_extraction.py +0 -0
  189. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/parity/saved_analysis.py +0 -0
  190. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/photodiode.py +0 -0
  191. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/photodiode_classification.py +0 -0
  192. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/pixel_calibration.py +0 -0
  193. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/pixel_calibration_profiles.py +0 -0
  194. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/response_roi_extraction.py +0 -0
  195. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/roi.py +0 -0
  196. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/roi_extraction.py +0 -0
  197. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/roi_mask_cleanup.py +0 -0
  198. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/session.py +0 -0
  199. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/spatial.py +0 -0
  200. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/stimulus.py +0 -0
  201. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/synchronization.py +0 -0
  202. {twopy-0.1.9 → twopy-0.2.0}/src/twopy/typing_guards.py +0 -0
  203. {twopy-0.1.9 → twopy-0.2.0}/src/twopy.egg-info/dependency_links.txt +0 -0
  204. {twopy-0.1.9 → twopy-0.2.0}/src/twopy.egg-info/entry_points.txt +0 -0
  205. {twopy-0.1.9 → twopy-0.2.0}/src/twopy.egg-info/requires.txt +0 -0
  206. {twopy-0.1.9 → twopy-0.2.0}/src/twopy.egg-info/top_level.txt +0 -0
  207. {twopy-0.1.9 → twopy-0.2.0}/tests/test_dff.py +0 -0
  208. {twopy-0.1.9 → twopy-0.2.0}/tests/test_filenames.py +0 -0
  209. {twopy-0.1.9 → twopy-0.2.0}/tests/test_frame_ranges.py +0 -0
  210. {twopy-0.1.9 → twopy-0.2.0}/tests/test_import.py +0 -0
  211. {twopy-0.1.9 → twopy-0.2.0}/tests/test_inspection.py +0 -0
  212. {twopy-0.1.9 → twopy-0.2.0}/tests/test_matlab.py +0 -0
  213. {twopy-0.1.9 → twopy-0.2.0}/tests/test_parity_roi_extraction.py +0 -0
  214. {twopy-0.1.9 → twopy-0.2.0}/tests/test_persistence.py +0 -0
  215. {twopy-0.1.9 → twopy-0.2.0}/tests/test_pixel_calibration.py +0 -0
  216. {twopy-0.1.9 → twopy-0.2.0}/tests/test_pixel_calibration_profiles.py +0 -0
  217. {twopy-0.1.9 → twopy-0.2.0}/tests/test_real_fixture.py +0 -0
  218. {twopy-0.1.9 → twopy-0.2.0}/tests/test_response_map_area.py +0 -0
  219. {twopy-0.1.9 → twopy-0.2.0}/tests/test_response_window_options.py +0 -0
  220. {twopy-0.1.9 → twopy-0.2.0}/tests/test_roi_extraction.py +0 -0
  221. {twopy-0.1.9 → twopy-0.2.0}/tests/test_roi_mask_cleanup.py +0 -0
  222. {twopy-0.1.9 → twopy-0.2.0}/tests/test_saved_analysis.py +0 -0
  223. {twopy-0.1.9 → twopy-0.2.0}/tests/test_session.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: twopy
3
- Version: 0.1.9
3
+ Version: 0.2.0
4
4
  Summary: Two-photon imaging analysis tool with a napari interface.
5
5
  Author-email: Gustavo Madeira Santana <fromweb1@gumadeiras.com>
6
6
  License-Expression: MIT
@@ -75,6 +75,7 @@ When a source recording has not been converted yet, twopy converts it first. Con
75
75
 
76
76
  - [GUI guide](docs/gui.md): napari loading, ROI editing, plotting, and saved outputs.
77
77
  - [Python API guide](docs/python_api.md): find recordings, convert data, run analysis, and open napari from scripts.
78
+ - [Custom workflow guide](docs/custom_workflows.md): add versioned local analyses to the napari Custom tab.
78
79
  - [Development guide](docs/development.md): source setup and local checks.
79
80
  - [Input data spec](docs/input_data_spec.md): short recording folder contract.
80
81
  - [Recording file reference](docs/recording_file_schema.md): detailed source and converted file schema.
@@ -55,6 +55,7 @@ When a source recording has not been converted yet, twopy converts it first. Con
55
55
 
56
56
  - [GUI guide](docs/gui.md): napari loading, ROI editing, plotting, and saved outputs.
57
57
  - [Python API guide](docs/python_api.md): find recordings, convert data, run analysis, and open napari from scripts.
58
+ - [Custom workflow guide](docs/custom_workflows.md): add versioned local analyses to the napari Custom tab.
58
59
  - [Development guide](docs/development.md): source setup and local checks.
59
60
  - [Input data spec](docs/input_data_spec.md): short recording folder contract.
60
61
  - [Recording file reference](docs/recording_file_schema.md): detailed source and converted file schema.
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "twopy"
7
- version = "0.1.9"
7
+ version = "0.2.0"
8
8
  description = "Two-photon imaging analysis tool with a napari interface."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.13,<3.14"
@@ -22,12 +22,17 @@ from twopy.analysis import (
22
22
  FrameWindow,
23
23
  GroupedRoiResponses,
24
24
  GroupedRoiResponseSummary,
25
- InterpolatedEpochMapping,
25
+ Hemisphere,
26
+ KernelFitMethod,
27
+ KernelStimulusModality,
26
28
  LoadedAnalysisOutputs,
27
29
  LowPassFilterOptions,
28
30
  ManualFovGroupRow,
31
+ ManualRoiMatchGroup,
29
32
  ManualRoiMatchRow,
30
33
  ManualRoiMatchStatus,
34
+ RecordingKernelFit,
35
+ RecordingTiming,
31
36
  ResponseMapData,
32
37
  ResponseMapMode,
33
38
  ResponseMapOptions,
@@ -37,7 +42,10 @@ from twopy.analysis import (
37
42
  RoiResponseSummary,
38
43
  RoiResponseTrial,
39
44
  SmoothingOptions,
45
+ StimulusKernelOptions,
46
+ TimingSource,
40
47
  WindowedRoiResponse,
48
+ add_manual_roi_match_group,
41
49
  analyze_recording_responses,
42
50
  append_manual_roi_match_rows,
43
51
  apply_motion_artifact_mask_to_delta_f_over_f,
@@ -45,11 +53,13 @@ from twopy.analysis import (
45
53
  compute_recording_responses,
46
54
  compute_roi_delta_f_over_f,
47
55
  default_baseline_epoch_number,
56
+ default_kernel_stimulus_column,
48
57
  default_recording_baseline_epoch_number,
49
58
  extract_background_corrected_roi_traces,
50
- frame_windows_from_photodiode_alignment,
59
+ finite_mean_and_sem,
60
+ fit_recording_stimulus_kernels,
61
+ fit_stimulus_kernel,
51
62
  group_delta_f_over_f_by_epoch,
52
- interpolate_stimulus_epochs_to_frame_windows,
53
63
  is_baseline_epoch_name,
54
64
  load_analysis_outputs,
55
65
  load_manual_fov_group_rows,
@@ -58,10 +68,13 @@ from twopy.analysis import (
58
68
  make_frame_windows,
59
69
  make_manual_fov_group_rows,
60
70
  make_manual_roi_match_rows,
61
- map_stimulus_epochs_to_frame_windows,
71
+ matched_manual_roi_groups,
62
72
  next_group_cell_id,
63
73
  no_baseline_epoch_frame_windows,
74
+ remove_manual_roi_match_group,
75
+ replace_manual_roi_match_group,
64
76
  resolve_baseline_frame_windows,
77
+ resolve_recording_timing,
65
78
  save_analysis_outputs,
66
79
  save_manual_fov_group_rows,
67
80
  save_manual_roi_match_rows,
@@ -76,7 +89,11 @@ from twopy.analysis import (
76
89
  )
77
90
  from twopy.api import find_recordings
78
91
  from twopy.conversion import convert_recording_to_twopy, load_source_conversion_inputs
79
- from twopy.converted import load_converted_recording, recording_frame_rate_hz
92
+ from twopy.converted import (
93
+ load_converted_recording,
94
+ recording_frame_rate_hz,
95
+ recording_hemisphere,
96
+ )
80
97
  from twopy.napari import (
81
98
  NapariRecordingView,
82
99
  NapariSidebarWidgets,
@@ -161,6 +178,7 @@ __all__ = [
161
178
  "AlignedPhotodiodeEvent",
162
179
  "AnalysisResponseRun",
163
180
  "AnalysisResponseComputation",
181
+ "add_manual_roi_match_group",
164
182
  "analyze_recording_responses",
165
183
  "add_twopy_magicgui_controls",
166
184
  "append_manual_roi_match_rows",
@@ -181,6 +199,7 @@ __all__ = [
181
199
  "DEFAULT_PIXEL_CALIBRATION_PATH",
182
200
  "DEFAULT_PIXEL_CALIBRATION_PROFILE_PATH",
183
201
  "default_baseline_epoch_number",
202
+ "default_kernel_stimulus_column",
184
203
  "default_recording_baseline_epoch_number",
185
204
  "detect_photodiode_events",
186
205
  "detect_recording_photodiode_events",
@@ -194,18 +213,20 @@ __all__ = [
194
213
  "extract_rois_from_image",
195
214
  "extract_roi_traces",
196
215
  "find_recordings",
197
- "frame_windows_from_photodiode_alignment",
216
+ "fit_recording_stimulus_kernels",
217
+ "fit_stimulus_kernel",
198
218
  "FrameWindow",
199
219
  "full_frame_crop",
200
220
  "group_delta_f_over_f_by_epoch",
201
221
  "GroupedRoiResponseSummary",
202
222
  "GroupedRoiResponses",
223
+ "Hemisphere",
203
224
  "grid_roi_set",
204
225
  "grid_roi_set_microns",
205
226
  "grid_size_pixels_from_microns",
206
- "InterpolatedEpochMapping",
207
- "interpolate_stimulus_epochs_to_frame_windows",
208
227
  "is_baseline_epoch_name",
228
+ "KernelFitMethod",
229
+ "KernelStimulusModality",
209
230
  "launch_napari",
210
231
  "load_analysis_outputs",
211
232
  "load_response_map_data",
@@ -219,16 +240,19 @@ __all__ = [
219
240
  "LoadedAnalysisOutputs",
220
241
  "LowPassFilterOptions",
221
242
  "ManualFovGroupRow",
243
+ "ManualRoiMatchGroup",
222
244
  "ManualRoiMatchRow",
223
245
  "ManualRoiMatchStatus",
246
+ "matched_manual_roi_groups",
224
247
  "make_roi_set_from_label_image",
225
248
  "make_frame_windows",
226
249
  "make_manual_fov_group_rows",
227
250
  "make_manual_roi_match_rows",
228
251
  "make_roi_set",
229
- "map_stimulus_epochs_to_frame_windows",
230
252
  "next_group_cell_id",
231
253
  "no_baseline_epoch_frame_windows",
254
+ "remove_manual_roi_match_group",
255
+ "replace_manual_roi_match_group",
232
256
  "map_stimulus_specific_column",
233
257
  "NapariRecordingView",
234
258
  "NapariSidebarWidgets",
@@ -255,10 +279,14 @@ __all__ = [
255
279
  "ResponseStatistic",
256
280
  "ResponseWatershedExtraction",
257
281
  "ResponseWatershedScoreImages",
282
+ "RecordingKernelFit",
283
+ "RecordingTiming",
258
284
  "recording_frame_rate_hz",
285
+ "recording_hemisphere",
259
286
  "resolve_pixel_size_um",
260
287
  "resolve_pixel_calibration_profile",
261
288
  "resolve_baseline_frame_windows",
289
+ "resolve_recording_timing",
262
290
  "response_watershed_roi_set",
263
291
  "RoiExtractionConfig",
264
292
  "RoiExtractionMethod",
@@ -282,10 +310,13 @@ __all__ = [
282
310
  "SmoothingOptions",
283
311
  "SpatialCrop",
284
312
  "SpatialDomain",
313
+ "StimulusKernelOptions",
285
314
  "StimulusSpecificColumnMapping",
286
315
  "stimulus_epoch_names_by_number",
316
+ "finite_mean_and_sem",
287
317
  "summarize_epoch_roi_responses",
288
318
  "summarize_grouped_responses",
319
+ "TimingSource",
289
320
  "validate_grouped_roi_responses",
290
321
  "watershed_roi_set",
291
322
  "WindowedRoiResponse",
@@ -19,23 +19,34 @@ from twopy.analysis.dff import (
19
19
  compute_roi_delta_f_over_f,
20
20
  )
21
21
  from twopy.analysis.dff_options import DeltaFOverFBaselineMode, DeltaFOverFOptions
22
- from twopy.analysis.epoch_mapping import (
23
- InterpolatedEpochMapping,
24
- interpolate_stimulus_epochs_to_frame_windows,
25
- )
26
22
  from twopy.analysis.group_matching import (
27
23
  ManualFovGroupRow,
24
+ ManualRoiMatchGroup,
28
25
  ManualRoiMatchRow,
29
26
  ManualRoiMatchStatus,
27
+ add_manual_roi_match_group,
30
28
  append_manual_roi_match_rows,
31
29
  load_manual_fov_group_rows,
32
30
  load_manual_roi_match_rows,
33
31
  make_manual_fov_group_rows,
34
32
  make_manual_roi_match_rows,
33
+ matched_manual_roi_groups,
35
34
  next_group_cell_id,
35
+ remove_manual_roi_match_group,
36
+ replace_manual_roi_match_group,
36
37
  save_manual_fov_group_rows,
37
38
  save_manual_roi_match_rows,
38
39
  )
40
+ from twopy.analysis.kernels import (
41
+ Hemisphere,
42
+ KernelFitMethod,
43
+ KernelStimulusModality,
44
+ RecordingKernelFit,
45
+ StimulusKernelOptions,
46
+ default_kernel_stimulus_column,
47
+ fit_recording_stimulus_kernels,
48
+ fit_stimulus_kernel,
49
+ )
39
50
  from twopy.analysis.motion import apply_motion_artifact_mask_to_delta_f_over_f
40
51
  from twopy.analysis.persistence import (
41
52
  ANALYSIS_OUTPUT_FILE_FORMAT,
@@ -67,20 +78,24 @@ from twopy.analysis.responses import (
67
78
  GroupedRoiResponseSummary,
68
79
  RoiResponseSummary,
69
80
  RoiResponseTrial,
81
+ finite_mean_and_sem,
70
82
  group_delta_f_over_f_by_epoch,
71
83
  summarize_epoch_roi_responses,
72
84
  summarize_grouped_responses,
73
85
  validate_grouped_roi_responses,
74
86
  )
87
+ from twopy.analysis.timing import (
88
+ RecordingTiming,
89
+ TimingSource,
90
+ resolve_recording_timing,
91
+ )
75
92
  from twopy.analysis.trials import (
76
93
  EpochFrameWindow,
77
94
  FrameWindow,
78
95
  WindowedRoiResponse,
79
96
  default_baseline_epoch_number,
80
- frame_windows_from_photodiode_alignment,
81
97
  is_baseline_epoch_name,
82
98
  make_frame_windows,
83
- map_stimulus_epochs_to_frame_windows,
84
99
  no_baseline_epoch_frame_windows,
85
100
  resolve_baseline_frame_windows,
86
101
  select_baseline_frame_windows,
@@ -117,27 +132,33 @@ __all__ = [
117
132
  "EpochResponseMap",
118
133
  "EpochFrameWindow",
119
134
  "extract_background_corrected_roi_traces",
120
- "frame_windows_from_photodiode_alignment",
135
+ "fit_recording_stimulus_kernels",
136
+ "fit_stimulus_kernel",
137
+ "default_kernel_stimulus_column",
138
+ "Hemisphere",
121
139
  "FrameWindow",
122
140
  "group_delta_f_over_f_by_epoch",
123
141
  "GroupedRoiResponseSummary",
124
142
  "GroupedRoiResponses",
125
- "InterpolatedEpochMapping",
126
- "interpolate_stimulus_epochs_to_frame_windows",
127
143
  "is_baseline_epoch_name",
144
+ "KernelFitMethod",
145
+ "KernelStimulusModality",
128
146
  "load_analysis_outputs",
129
147
  "load_response_map_data",
130
148
  "LoadedAnalysisOutputs",
131
149
  "LowPassFilterOptions",
132
150
  "ManualFovGroupRow",
151
+ "ManualRoiMatchGroup",
133
152
  "ManualRoiMatchRow",
134
153
  "ManualRoiMatchStatus",
154
+ "matched_manual_roi_groups",
135
155
  "make_frame_windows",
136
156
  "make_manual_fov_group_rows",
137
157
  "make_manual_roi_match_rows",
138
- "map_stimulus_epochs_to_frame_windows",
139
158
  "no_baseline_epoch_frame_windows",
140
159
  "next_group_cell_id",
160
+ "RecordingKernelFit",
161
+ "RecordingTiming",
141
162
  "RoiDeltaFOverF",
142
163
  "ResponseMapData",
143
164
  "ResponseMapMode",
@@ -147,18 +168,25 @@ __all__ = [
147
168
  "RoiResponseSummary",
148
169
  "RoiResponseTrial",
149
170
  "resolve_baseline_frame_windows",
171
+ "resolve_recording_timing",
172
+ "add_manual_roi_match_group",
150
173
  "append_manual_roi_match_rows",
151
174
  "load_manual_fov_group_rows",
152
175
  "load_manual_roi_match_rows",
176
+ "remove_manual_roi_match_group",
177
+ "replace_manual_roi_match_group",
153
178
  "save_analysis_outputs",
154
179
  "save_response_map_data",
155
180
  "save_manual_fov_group_rows",
156
181
  "save_manual_roi_match_rows",
157
182
  "select_baseline_frame_windows",
183
+ "finite_mean_and_sem",
158
184
  "split_traces_by_frame_windows",
185
+ "StimulusKernelOptions",
159
186
  "SmoothingOptions",
160
187
  "summarize_epoch_roi_responses",
161
188
  "summarize_grouped_responses",
189
+ "TimingSource",
162
190
  "validate_grouped_roi_responses",
163
191
  "WindowedRoiResponse",
164
192
  "write_response_summary_grouped_csv",
@@ -17,14 +17,19 @@ from typing import Literal
17
17
 
18
18
  __all__ = [
19
19
  "ManualFovGroupRow",
20
+ "ManualRoiMatchGroup",
20
21
  "ManualRoiMatchRow",
21
22
  "ManualRoiMatchStatus",
23
+ "add_manual_roi_match_group",
22
24
  "append_manual_roi_match_rows",
23
25
  "load_manual_fov_group_rows",
24
26
  "load_manual_roi_match_rows",
25
27
  "make_manual_fov_group_rows",
26
28
  "make_manual_roi_match_rows",
29
+ "matched_manual_roi_groups",
27
30
  "next_group_cell_id",
31
+ "remove_manual_roi_match_group",
32
+ "replace_manual_roi_match_group",
28
33
  "save_manual_fov_group_rows",
29
34
  "save_manual_roi_match_rows",
30
35
  ]
@@ -87,6 +92,19 @@ class ManualRoiMatchRow:
87
92
  note: str = ""
88
93
 
89
94
 
95
+ @dataclass(frozen=True)
96
+ class ManualRoiMatchGroup:
97
+ """Matched rows for one manually assigned cell group.
98
+
99
+ Inputs: existing ``ManualRoiMatchRow`` objects from a match table.
100
+ Outputs: one grouped decision for display or downstream group analysis.
101
+ """
102
+
103
+ group_cell_id: int
104
+ rows: tuple[ManualRoiMatchRow, ...]
105
+ note: str = ""
106
+
107
+
90
108
  def make_manual_fov_group_rows(
91
109
  recording_groups: Mapping[Path, str],
92
110
  *,
@@ -171,6 +189,112 @@ def make_manual_roi_match_rows(
171
189
  return tuple(rows)
172
190
 
173
191
 
192
+ def matched_manual_roi_groups(
193
+ rows: Sequence[ManualRoiMatchRow],
194
+ *,
195
+ fov_group_id: str,
196
+ ) -> tuple[ManualRoiMatchGroup, ...]:
197
+ """Return matched ROI groups for one FOV id.
198
+
199
+ Args:
200
+ rows: Manual ROI match rows from memory or CSV.
201
+ fov_group_id: FOV id whose matched groups should be returned.
202
+
203
+ Returns:
204
+ Groups sorted by ``group_cell_id``. Non-matched statuses and other FOVs
205
+ are ignored.
206
+ """
207
+ _validate_fov_group_id(fov_group_id)
208
+ grouped: dict[int, list[ManualRoiMatchRow]] = {}
209
+ for row in rows:
210
+ if row.status != "matched" or row.fov_group_id != fov_group_id:
211
+ continue
212
+ grouped.setdefault(row.group_cell_id, []).append(row)
213
+ groups: list[ManualRoiMatchGroup] = []
214
+ for group_cell_id, group_rows in sorted(grouped.items()):
215
+ row_tuple = tuple(group_rows)
216
+ groups.append(
217
+ ManualRoiMatchGroup(
218
+ group_cell_id=group_cell_id,
219
+ rows=row_tuple,
220
+ note=_shared_row_note(row_tuple),
221
+ ),
222
+ )
223
+ return tuple(groups)
224
+
225
+
226
+ def add_manual_roi_match_group(
227
+ rows: Sequence[ManualRoiMatchRow],
228
+ recording_rois: Mapping[Path, str],
229
+ *,
230
+ fov_group_id: str,
231
+ note: str = "",
232
+ ) -> tuple[tuple[ManualRoiMatchRow, ...], int]:
233
+ """Append one new matched ROI group to existing rows.
234
+
235
+ Args:
236
+ rows: Existing manual ROI match rows.
237
+ recording_rois: Mapping from recording path to selected ROI label.
238
+ fov_group_id: FOV id for the new matched group.
239
+ note: Optional note copied to every new row.
240
+
241
+ Returns:
242
+ Updated row tuple and the assigned ``group_cell_id``.
243
+ """
244
+ _validate_fov_group_id(fov_group_id)
245
+ group_cell_id = next_group_cell_id(rows)
246
+ group_rows = make_manual_roi_match_rows(
247
+ recording_rois,
248
+ group_cell_id=group_cell_id,
249
+ fov_group_id=fov_group_id,
250
+ status="matched",
251
+ note=note,
252
+ )
253
+ return (*tuple(rows), *group_rows), group_cell_id
254
+
255
+
256
+ def replace_manual_roi_match_group(
257
+ rows: Sequence[ManualRoiMatchRow],
258
+ recording_rois: Mapping[Path, str],
259
+ *,
260
+ fov_group_id: str,
261
+ group_cell_id: int,
262
+ note: str = "",
263
+ ) -> tuple[ManualRoiMatchRow, ...]:
264
+ """Replace one matched group inside one FOV while preserving other rows.
265
+
266
+ Args:
267
+ rows: Existing manual ROI match rows.
268
+ recording_rois: Replacement recording-to-ROI selections.
269
+ fov_group_id: FOV id containing the group.
270
+ group_cell_id: Existing group id to replace.
271
+ note: Optional note copied to every replacement row.
272
+
273
+ Returns:
274
+ Updated row tuple.
275
+ """
276
+ _validate_fov_group_id(fov_group_id)
277
+ replacement_rows = make_manual_roi_match_rows(
278
+ recording_rois,
279
+ group_cell_id=group_cell_id,
280
+ fov_group_id=fov_group_id,
281
+ status="matched",
282
+ note=note,
283
+ )
284
+ retained_rows = _rows_without_matched_group(rows, fov_group_id, group_cell_id)
285
+ return (*retained_rows, *replacement_rows)
286
+
287
+
288
+ def remove_manual_roi_match_group(
289
+ rows: Sequence[ManualRoiMatchRow],
290
+ *,
291
+ fov_group_id: str,
292
+ group_cell_id: int,
293
+ ) -> tuple[ManualRoiMatchRow, ...]:
294
+ """Remove one matched group inside one FOV while preserving other rows."""
295
+ return _rows_without_matched_group(rows, fov_group_id, group_cell_id)
296
+
297
+
174
298
  def save_manual_fov_group_rows(
175
299
  rows: Iterable[ManualFovGroupRow],
176
300
  path: Path,
@@ -289,6 +413,31 @@ def next_group_cell_id(rows: Sequence[ManualRoiMatchRow]) -> int:
289
413
  return max(row.group_cell_id for row in rows) + 1
290
414
 
291
415
 
416
+ def _rows_without_matched_group(
417
+ rows: Sequence[ManualRoiMatchRow],
418
+ fov_group_id: str,
419
+ group_cell_id: int,
420
+ ) -> tuple[ManualRoiMatchRow, ...]:
421
+ """Return rows after removing one matched group in one FOV."""
422
+ _validate_fov_group_id(fov_group_id)
423
+ _validate_group_cell_id(group_cell_id)
424
+ return tuple(
425
+ row
426
+ for row in rows
427
+ if not (
428
+ row.status == "matched"
429
+ and row.fov_group_id == fov_group_id
430
+ and row.group_cell_id == group_cell_id
431
+ )
432
+ )
433
+
434
+
435
+ def _shared_row_note(rows: tuple[ManualRoiMatchRow, ...]) -> str:
436
+ """Return the common note for one matched group."""
437
+ notes = {row.note for row in rows}
438
+ return notes.pop() if len(notes) == 1 else ""
439
+
440
+
292
441
  def _write_csv_rows(
293
442
  path: Path,
294
443
  *,