twopy 0.1.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 (116) hide show
  1. twopy-0.1.0/PKG-INFO +311 -0
  2. twopy-0.1.0/README.md +291 -0
  3. twopy-0.1.0/pyproject.toml +67 -0
  4. twopy-0.1.0/setup.cfg +4 -0
  5. twopy-0.1.0/src/twopy/__init__.py +168 -0
  6. twopy-0.1.0/src/twopy/_segments.py +53 -0
  7. twopy-0.1.0/src/twopy/analysis/__init__.py +101 -0
  8. twopy-0.1.0/src/twopy/analysis/background_subtraction.py +672 -0
  9. twopy-0.1.0/src/twopy/analysis/dff.py +524 -0
  10. twopy-0.1.0/src/twopy/analysis/epoch_mapping.py +223 -0
  11. twopy-0.1.0/src/twopy/analysis/motion.py +66 -0
  12. twopy-0.1.0/src/twopy/analysis/persistence.py +975 -0
  13. twopy-0.1.0/src/twopy/analysis/response_processing/__init__.py +55 -0
  14. twopy-0.1.0/src/twopy/analysis/response_processing/apply.py +298 -0
  15. twopy-0.1.0/src/twopy/analysis/response_processing/options.py +192 -0
  16. twopy-0.1.0/src/twopy/analysis/response_processing/qc.py +270 -0
  17. twopy-0.1.0/src/twopy/analysis/response_processing/signals.py +200 -0
  18. twopy-0.1.0/src/twopy/analysis/responses.py +345 -0
  19. twopy-0.1.0/src/twopy/analysis/trials.py +341 -0
  20. twopy-0.1.0/src/twopy/analysis/workflow.py +457 -0
  21. twopy-0.1.0/src/twopy/api.py +78 -0
  22. twopy-0.1.0/src/twopy/config.py +200 -0
  23. twopy-0.1.0/src/twopy/conversion/__init__.py +40 -0
  24. twopy-0.1.0/src/twopy/conversion/alignment_crop.py +329 -0
  25. twopy-0.1.0/src/twopy/conversion/api.py +113 -0
  26. twopy-0.1.0/src/twopy/conversion/frame_ranges.py +35 -0
  27. twopy-0.1.0/src/twopy/conversion/hdf5_writing.py +362 -0
  28. twopy-0.1.0/src/twopy/conversion/matlab_values.py +123 -0
  29. twopy-0.1.0/src/twopy/conversion/source_loading.py +403 -0
  30. twopy-0.1.0/src/twopy/conversion/stimulus_code.py +248 -0
  31. twopy-0.1.0/src/twopy/conversion/types.py +295 -0
  32. twopy-0.1.0/src/twopy/converted.py +681 -0
  33. twopy-0.1.0/src/twopy/database/__init__.py +33 -0
  34. twopy-0.1.0/src/twopy/database/cache.py +166 -0
  35. twopy-0.1.0/src/twopy/database/catalog.py +90 -0
  36. twopy-0.1.0/src/twopy/database/connection.py +27 -0
  37. twopy-0.1.0/src/twopy/database/generic.py +254 -0
  38. twopy-0.1.0/src/twopy/database/modeled.py +419 -0
  39. twopy-0.1.0/src/twopy/database/sql.py +46 -0
  40. twopy-0.1.0/src/twopy/database/types.py +116 -0
  41. twopy-0.1.0/src/twopy/inspection.py +492 -0
  42. twopy-0.1.0/src/twopy/matlab.py +398 -0
  43. twopy-0.1.0/src/twopy/napari/__init__.py +27 -0
  44. twopy-0.1.0/src/twopy/napari/__main__.py +10 -0
  45. twopy-0.1.0/src/twopy/napari/constants.py +13 -0
  46. twopy-0.1.0/src/twopy/napari/controls.py +739 -0
  47. twopy-0.1.0/src/twopy/napari/display.py +181 -0
  48. twopy-0.1.0/src/twopy/napari/display_paths.py +197 -0
  49. twopy-0.1.0/src/twopy/napari/interactive.py +310 -0
  50. twopy-0.1.0/src/twopy/napari/launcher.py +175 -0
  51. twopy-0.1.0/src/twopy/napari/layout.py +147 -0
  52. twopy-0.1.0/src/twopy/napari/loading.py +207 -0
  53. twopy-0.1.0/src/twopy/napari/movie.py +71 -0
  54. twopy-0.1.0/src/twopy/napari/paths.py +181 -0
  55. twopy-0.1.0/src/twopy/napari/plotting/__init__.py +21 -0
  56. twopy-0.1.0/src/twopy/napari/plotting/data.py +266 -0
  57. twopy-0.1.0/src/twopy/napari/plotting/docks.py +1052 -0
  58. twopy-0.1.0/src/twopy/napari/plotting/export.py +803 -0
  59. twopy-0.1.0/src/twopy/napari/plotting/export_controls.py +209 -0
  60. twopy-0.1.0/src/twopy/napari/plotting/label_visibility.py +260 -0
  61. twopy-0.1.0/src/twopy/napari/plotting/options.py +269 -0
  62. twopy-0.1.0/src/twopy/napari/plotting/panels.py +119 -0
  63. twopy-0.1.0/src/twopy/napari/plotting/widgets.py +767 -0
  64. twopy-0.1.0/src/twopy/napari/protocols.py +90 -0
  65. twopy-0.1.0/src/twopy/napari/responses.py +91 -0
  66. twopy-0.1.0/src/twopy/napari/roi.py +238 -0
  67. twopy-0.1.0/src/twopy/napari/session.py +381 -0
  68. twopy-0.1.0/src/twopy/napari/state.py +106 -0
  69. twopy-0.1.0/src/twopy/napari/text.py +26 -0
  70. twopy-0.1.0/src/twopy/napari/types.py +39 -0
  71. twopy-0.1.0/src/twopy/napari/viewer.py +405 -0
  72. twopy-0.1.0/src/twopy/parity/__init__.py +30 -0
  73. twopy-0.1.0/src/twopy/parity/dff.py +399 -0
  74. twopy-0.1.0/src/twopy/parity/roi.py +145 -0
  75. twopy-0.1.0/src/twopy/parity/saved_analysis.py +174 -0
  76. twopy-0.1.0/src/twopy/photodiode.py +375 -0
  77. twopy-0.1.0/src/twopy/photodiode_classification.py +484 -0
  78. twopy-0.1.0/src/twopy/roi.py +462 -0
  79. twopy-0.1.0/src/twopy/session.py +167 -0
  80. twopy-0.1.0/src/twopy/spatial.py +154 -0
  81. twopy-0.1.0/src/twopy/stimulus.py +233 -0
  82. twopy-0.1.0/src/twopy/synchronization.py +235 -0
  83. twopy-0.1.0/src/twopy/typing_guards.py +255 -0
  84. twopy-0.1.0/src/twopy.egg-info/PKG-INFO +311 -0
  85. twopy-0.1.0/src/twopy.egg-info/SOURCES.txt +114 -0
  86. twopy-0.1.0/src/twopy.egg-info/dependency_links.txt +1 -0
  87. twopy-0.1.0/src/twopy.egg-info/entry_points.txt +2 -0
  88. twopy-0.1.0/src/twopy.egg-info/requires.txt +11 -0
  89. twopy-0.1.0/src/twopy.egg-info/top_level.txt +1 -0
  90. twopy-0.1.0/tests/test_api.py +129 -0
  91. twopy-0.1.0/tests/test_background_subtraction.py +461 -0
  92. twopy-0.1.0/tests/test_config.py +171 -0
  93. twopy-0.1.0/tests/test_conversion.py +675 -0
  94. twopy-0.1.0/tests/test_converted.py +327 -0
  95. twopy-0.1.0/tests/test_database.py +292 -0
  96. twopy-0.1.0/tests/test_dff.py +249 -0
  97. twopy-0.1.0/tests/test_epoch_mapping.py +112 -0
  98. twopy-0.1.0/tests/test_import.py +25 -0
  99. twopy-0.1.0/tests/test_inspection.py +98 -0
  100. twopy-0.1.0/tests/test_matlab.py +96 -0
  101. twopy-0.1.0/tests/test_motion.py +104 -0
  102. twopy-0.1.0/tests/test_napari.py +2224 -0
  103. twopy-0.1.0/tests/test_packaging.py +32 -0
  104. twopy-0.1.0/tests/test_parity.py +208 -0
  105. twopy-0.1.0/tests/test_persistence.py +312 -0
  106. twopy-0.1.0/tests/test_photodiode_classification.py +223 -0
  107. twopy-0.1.0/tests/test_real_fixture.py +92 -0
  108. twopy-0.1.0/tests/test_response_processing.py +254 -0
  109. twopy-0.1.0/tests/test_responses.py +180 -0
  110. twopy-0.1.0/tests/test_roi.py +240 -0
  111. twopy-0.1.0/tests/test_saved_analysis.py +90 -0
  112. twopy-0.1.0/tests/test_session.py +111 -0
  113. twopy-0.1.0/tests/test_stimulus.py +164 -0
  114. twopy-0.1.0/tests/test_synchronization.py +178 -0
  115. twopy-0.1.0/tests/test_trials.py +293 -0
  116. twopy-0.1.0/tests/test_workflow.py +221 -0
twopy-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,311 @@
1
+ Metadata-Version: 2.4
2
+ Name: twopy
3
+ Version: 0.1.0
4
+ Summary: Two-photon imaging analysis tool with a napari interface.
5
+ Author-email: Gustavo Madeira Santana <fromweb1@gumadeiras.com>
6
+ License-Expression: MIT
7
+ Requires-Python: <3.14,>=3.13
8
+ Description-Content-Type: text/markdown
9
+ Requires-Dist: h5py<4,>=3.16
10
+ Requires-Dist: matplotlib<4,>=3.10
11
+ Requires-Dist: napari[all]<0.8,>=0.7
12
+ Requires-Dist: numpy<3,>=2.3
13
+ Requires-Dist: pre-commit<5,>=4.6
14
+ Requires-Dist: qtpy<3,>=2.4
15
+ Requires-Dist: pyyaml<7,>=6.0.3
16
+ Requires-Dist: ruff>=0.15
17
+ Requires-Dist: scipy<2,>=1.17
18
+ Requires-Dist: tifffile<2027,>=2026.3
19
+ Requires-Dist: ty<0.1,>=0.0.34
20
+
21
+ # twopy
22
+
23
+ Two-photon imaging analysis tool for the Clark Lab output format.
24
+
25
+ ## Getting Started
26
+
27
+ twopy lets you open two-photon recordings, draw ROIs, plot responses in real time,
28
+ process and analyze them, and save them.
29
+
30
+ When you first load a recording, twopy converts it to a standardized HDF5 format.
31
+ The converted format includes the aligned movie, mean image, stimulus tables,
32
+ photodiode signals, and recording metadata. Analysis and the GUI both work from
33
+ the converted files, so the original source files remain separate from twopy's outputs.
34
+
35
+ ### Install
36
+
37
+ Examples use micromamba, but any conda-compatible environment manager should work.
38
+
39
+ Run this once from the twopy folder to install everything you need:
40
+
41
+ ```sh
42
+ micromamba env create -f environment.yml
43
+ micromamba activate twopy
44
+ micromamba run -n twopy pre-commit install
45
+ cp config.example.yml config.yml
46
+ ```
47
+
48
+ Then edit `config.yml` so the paths match your computer. The example file
49
+ explains each setting in plain language. `config.yml` stays local to your
50
+ machine and is not tracked by git.
51
+
52
+ If the `twopy` command is missing after setup, refresh the editable install:
53
+
54
+ ```sh
55
+ micromamba run -n twopy python -m pip install -e .
56
+ ```
57
+
58
+ ### Start The GUI
59
+
60
+ Start napari from the twopy environment:
61
+
62
+ ```sh
63
+ micromamba activate twopy
64
+ twopy
65
+ ```
66
+
67
+ Or run it without activating the environment first:
68
+
69
+ ```sh
70
+ micromamba run -n twopy twopy
71
+ ```
72
+
73
+ You can also open a recording directly:
74
+
75
+ ```sh
76
+ twopy /path/to/source/recording
77
+ ```
78
+
79
+ Or direct path to converted HDF5 files:
80
+
81
+ ```sh
82
+ twopy /path/to/recording_data.h5
83
+ ```
84
+
85
+ Inside napari, use the `twopy` panel to choose a recording folder or a
86
+ `recording_data.h5` file. If a source recording has not been converted yet,
87
+ twopy converts it first, then opens the converted files.
88
+
89
+ Basic GUI flow:
90
+
91
+ 1. Start twopy.
92
+ 2. Choose a recording.
93
+ 3. Draw or edit ROIs in the `rois` Labels layer.
94
+ 4. Click Save ROIs.
95
+ 5. Use the response plot panel to update plots from the current ROIs.
96
+
97
+ ## Setup Details
98
+
99
+ The environment installs twopy as an editable package, so the `twopy` terminal
100
+ command is available after activating the environment. If the environment
101
+ already existed before the command was added, refresh the editable install:
102
+
103
+ ```sh
104
+ micromamba run -n twopy python -m pip install -e .
105
+ ```
106
+
107
+ ## Check
108
+
109
+ ```sh
110
+ micromamba run -n twopy pre-commit run --all-files
111
+ ```
112
+
113
+ The installed pre-commit hook runs ruff, ty, and the unit tests before each
114
+ commit.
115
+
116
+ ## Release
117
+
118
+ PyPI publishing uses GitHub Actions Trusted Publishing. No PyPI API token secret
119
+ is required.
120
+
121
+ One-time setup in PyPI:
122
+
123
+ 1. Add a trusted publisher for the `twopy` project.
124
+ 2. Use owner `gumadeiras`, repository `twopy`, workflow
125
+ `publish-to-pypi.yml`, and environment `pypi`.
126
+ 3. In GitHub, create the `pypi` environment and require manual approval.
127
+
128
+ Release flow:
129
+
130
+ 1. Update `project.version` in `pyproject.toml`.
131
+ 2. Run `micromamba run -n twopy pre-commit run --all-files`.
132
+ 3. Commit the version change.
133
+ 4. Create a GitHub release whose tag is the same version, with or without a
134
+ leading `v`.
135
+ 5. Publish the release.
136
+
137
+ The release workflow checks that the tag matches `pyproject.toml`, builds the
138
+ wheel and source distribution, checks package metadata, and publishes to PyPI
139
+ after the `pypi` environment approval.
140
+
141
+ ## Find Recordings
142
+
143
+ ```python
144
+ from twopy import find_recordings
145
+
146
+ recordings = find_recordings(
147
+ year=2023,
148
+ month=10,
149
+ day=17,
150
+ genotype="gh146",
151
+ stimulus="combo_stim",
152
+ sensor="g6f",
153
+ cell_type="ALPN",
154
+ hemisphere="right",
155
+ person="Gustavo",
156
+ )
157
+ ```
158
+
159
+ `config.yml` controls whether DB queries use mounted files directly or cached
160
+ local copies. The default is `database_access: copy` because database searches
161
+ over the network can be slow, while copying the DB file locally is usually fast.
162
+
163
+ ## Convert Recording
164
+
165
+ ```python
166
+ from pathlib import Path
167
+
168
+ from twopy import convert_recording_to_twopy
169
+
170
+ recording = Path("/path/to/recording")
171
+
172
+ converted = convert_recording_to_twopy(recording)
173
+ print(converted.path)
174
+ print(converted.movie_path)
175
+ ```
176
+
177
+ Conversion writes `recording_data.h5` for metadata, stimulus tables,
178
+ photodiode signals, and the mean image. The large aligned movie is written
179
+ separately to `aligned_movie.h5`. By default the mean image uses the full movie;
180
+ pass `mean_start_frame` and `mean_stop_frame` to use a frame range. By default,
181
+ conversion writes to the location configured by `analysis_output`; pass
182
+ `output_dir` only when you want to override that for a specific call.
183
+
184
+ `config.yml` also controls analysis output routing. `analysis_output: source`
185
+ writes into `recording/twopy`; a path mirrors the recording directory structure
186
+ under that output root.
187
+
188
+ ## Analyze Converted Data
189
+
190
+ ```python
191
+ from pathlib import Path
192
+
193
+ import numpy as np
194
+
195
+ from twopy import (
196
+ classify_recording_photodiode_events,
197
+ compute_roi_delta_f_over_f,
198
+ detect_recording_photodiode_events,
199
+ extract_background_corrected_roi_traces,
200
+ load_converted_recording,
201
+ make_roi_set,
202
+ map_stimulus_epochs_to_frame_windows,
203
+ select_epoch_frame_windows,
204
+ )
205
+
206
+ recording = load_converted_recording(Path("/path/to/recording_data.h5"))
207
+ mask_array = np.zeros((1, *recording.movie.shape[1:]), dtype=bool)
208
+ mask_array[0, :10, :10] = True
209
+ roi_set = make_roi_set(mask_array)
210
+ traces = extract_background_corrected_roi_traces(
211
+ recording,
212
+ roi_set,
213
+ method="movie_global_percentile",
214
+ )
215
+ alignment = detect_recording_photodiode_events(recording)
216
+ timing = classify_recording_photodiode_events(recording, alignment)
217
+ epoch_windows = map_stimulus_epochs_to_frame_windows(recording, alignment)
218
+ interleave_windows = select_epoch_frame_windows(
219
+ epoch_windows,
220
+ epoch_name="Gray Interleave",
221
+ )
222
+ dff = compute_roi_delta_f_over_f(
223
+ traces,
224
+ interleave_windows,
225
+ data_rate_hz=float(recording.acquisition_metadata["acq.frameRate"]),
226
+ fit_mode="robust",
227
+ )
228
+ ```
229
+
230
+ Analysis starts from converted HDF5 files. ROI masks are GUI-independent, trace
231
+ extraction streams movie chunks, background correction stays explicit, and
232
+ stimulus epoch windows come from classified photodiode events instead of
233
+ nominal frame-rate assumptions. `timing.events` keeps the start, transition,
234
+ and end classifications auditable. Analysis trace extraction uses the saved
235
+ alignment-valid spatial crop by default, including `method="none"` when you
236
+ want uncorrected traces through the analysis path. ROI masks remain full-frame;
237
+ pass `spatial_domain="full_frame"` only when that is the intended audit path.
238
+ The lower-level `extract_roi_traces` helper is a full-frame raw primitive. ROI
239
+ dF/F uses corrected ROI fluorescence plus gray interleave windows to fit one
240
+ shared exponential tau and one amplitude per ROI. The default dF/F fit mode is
241
+ `robust`; pass `fit_mode="source_bounds"` when you need original source-bound
242
+ behavior for audit comparisons.
243
+
244
+ ## Open In Napari
245
+
246
+ From a converted output directory that contains `recording_data.h5`, or from a
247
+ source recording directory:
248
+
249
+ ```sh
250
+ twopy
251
+ ```
252
+
253
+ If converted files are missing and the selected folder has the expected source
254
+ recording files, twopy runs conversion first and then opens the converted HDF5
255
+ files. If no recording is found, `twopy` still opens napari. Choose a recording
256
+ folder or `recording_data.h5` in the `twopy` dock panel; twopy loads it after
257
+ selection.
258
+
259
+ Or pass a source folder or converted recording explicitly:
260
+
261
+ ```sh
262
+ twopy /path/to/source/recording
263
+ twopy /path/to/recording_data.h5
264
+ ```
265
+
266
+ By default the launcher opens the mean image, the full movie, an editable
267
+ `rois` Labels layer, a top response-plot dock, a `twopy` loading dock, and a left
268
+ Save ROIs dock. Use `--no-movie` to skip the movie preview, or `--movie-start`
269
+ and `--movie-end` to choose a different preview range. Save ROIs writes
270
+ `rois.h5` beside the current recording by default. The response dock can reload
271
+ existing `analysis_outputs.h5` or update plots from the current Labels layer.
272
+ Saving analysis writes `rois.h5`, `analysis_outputs.h5`,
273
+ `exports/csvs/response_summary_trials.csv`, and
274
+ `exports/csvs/response_summary_grouped.csv` beside the converted recording.
275
+ Response plots share one y-axis across epochs and show two seconds before
276
+ stimulus onset and two seconds after stimulus offset by default, when gray
277
+ interleave frames are available in the grouped responses. Epoch plots are laid
278
+ out horizontally. Each saved response trial includes its own `time_seconds`
279
+ vector, so plots use direct response time values rather than inferring time from
280
+ array indices.
281
+
282
+ ```python
283
+ from pathlib import Path
284
+
285
+ from twopy import (
286
+ launch_napari,
287
+ open_recording_in_napari,
288
+ roi_label_image_from_layer,
289
+ save_napari_label_rois,
290
+ )
291
+
292
+ launch_napari(Path("/path/to/recording_data.h5"))
293
+
294
+ view = open_recording_in_napari(
295
+ Path("/path/to/recording_data.h5"),
296
+ movie_frame_range=(0, 200),
297
+ )
298
+
299
+ # After drawing or editing the rois Labels layer:
300
+ label_image = roi_label_image_from_layer(view.roi_labels_layer)
301
+ roi_set = save_napari_label_rois(label_image, Path("/path/to/rois.h5"))
302
+ ```
303
+
304
+ Napari code is a thin adapter. It loads converted twopy files, displays the
305
+ mean image, optionally displays a bounded movie preview, creates an editable
306
+ ROI Labels layer, and adds small dock widgets for loading folders, saving ROIs,
307
+ and plotting responses. ROI saving writes the current Labels layer through the
308
+ core ROI HDF5 helpers. Response plotting calls the core analysis workflow when
309
+ updating from current ROIs. Pass `roi_set=Path("/path/to/rois.h5")` when
310
+ reopening existing ROIs. Napari code does not read source MATLAB/TIFF files or
311
+ own analysis decisions.
twopy-0.1.0/README.md ADDED
@@ -0,0 +1,291 @@
1
+ # twopy
2
+
3
+ Two-photon imaging analysis tool for the Clark Lab output format.
4
+
5
+ ## Getting Started
6
+
7
+ twopy lets you open two-photon recordings, draw ROIs, plot responses in real time,
8
+ process and analyze them, and save them.
9
+
10
+ When you first load a recording, twopy converts it to a standardized HDF5 format.
11
+ The converted format includes the aligned movie, mean image, stimulus tables,
12
+ photodiode signals, and recording metadata. Analysis and the GUI both work from
13
+ the converted files, so the original source files remain separate from twopy's outputs.
14
+
15
+ ### Install
16
+
17
+ Examples use micromamba, but any conda-compatible environment manager should work.
18
+
19
+ Run this once from the twopy folder to install everything you need:
20
+
21
+ ```sh
22
+ micromamba env create -f environment.yml
23
+ micromamba activate twopy
24
+ micromamba run -n twopy pre-commit install
25
+ cp config.example.yml config.yml
26
+ ```
27
+
28
+ Then edit `config.yml` so the paths match your computer. The example file
29
+ explains each setting in plain language. `config.yml` stays local to your
30
+ machine and is not tracked by git.
31
+
32
+ If the `twopy` command is missing after setup, refresh the editable install:
33
+
34
+ ```sh
35
+ micromamba run -n twopy python -m pip install -e .
36
+ ```
37
+
38
+ ### Start The GUI
39
+
40
+ Start napari from the twopy environment:
41
+
42
+ ```sh
43
+ micromamba activate twopy
44
+ twopy
45
+ ```
46
+
47
+ Or run it without activating the environment first:
48
+
49
+ ```sh
50
+ micromamba run -n twopy twopy
51
+ ```
52
+
53
+ You can also open a recording directly:
54
+
55
+ ```sh
56
+ twopy /path/to/source/recording
57
+ ```
58
+
59
+ Or direct path to converted HDF5 files:
60
+
61
+ ```sh
62
+ twopy /path/to/recording_data.h5
63
+ ```
64
+
65
+ Inside napari, use the `twopy` panel to choose a recording folder or a
66
+ `recording_data.h5` file. If a source recording has not been converted yet,
67
+ twopy converts it first, then opens the converted files.
68
+
69
+ Basic GUI flow:
70
+
71
+ 1. Start twopy.
72
+ 2. Choose a recording.
73
+ 3. Draw or edit ROIs in the `rois` Labels layer.
74
+ 4. Click Save ROIs.
75
+ 5. Use the response plot panel to update plots from the current ROIs.
76
+
77
+ ## Setup Details
78
+
79
+ The environment installs twopy as an editable package, so the `twopy` terminal
80
+ command is available after activating the environment. If the environment
81
+ already existed before the command was added, refresh the editable install:
82
+
83
+ ```sh
84
+ micromamba run -n twopy python -m pip install -e .
85
+ ```
86
+
87
+ ## Check
88
+
89
+ ```sh
90
+ micromamba run -n twopy pre-commit run --all-files
91
+ ```
92
+
93
+ The installed pre-commit hook runs ruff, ty, and the unit tests before each
94
+ commit.
95
+
96
+ ## Release
97
+
98
+ PyPI publishing uses GitHub Actions Trusted Publishing. No PyPI API token secret
99
+ is required.
100
+
101
+ One-time setup in PyPI:
102
+
103
+ 1. Add a trusted publisher for the `twopy` project.
104
+ 2. Use owner `gumadeiras`, repository `twopy`, workflow
105
+ `publish-to-pypi.yml`, and environment `pypi`.
106
+ 3. In GitHub, create the `pypi` environment and require manual approval.
107
+
108
+ Release flow:
109
+
110
+ 1. Update `project.version` in `pyproject.toml`.
111
+ 2. Run `micromamba run -n twopy pre-commit run --all-files`.
112
+ 3. Commit the version change.
113
+ 4. Create a GitHub release whose tag is the same version, with or without a
114
+ leading `v`.
115
+ 5. Publish the release.
116
+
117
+ The release workflow checks that the tag matches `pyproject.toml`, builds the
118
+ wheel and source distribution, checks package metadata, and publishes to PyPI
119
+ after the `pypi` environment approval.
120
+
121
+ ## Find Recordings
122
+
123
+ ```python
124
+ from twopy import find_recordings
125
+
126
+ recordings = find_recordings(
127
+ year=2023,
128
+ month=10,
129
+ day=17,
130
+ genotype="gh146",
131
+ stimulus="combo_stim",
132
+ sensor="g6f",
133
+ cell_type="ALPN",
134
+ hemisphere="right",
135
+ person="Gustavo",
136
+ )
137
+ ```
138
+
139
+ `config.yml` controls whether DB queries use mounted files directly or cached
140
+ local copies. The default is `database_access: copy` because database searches
141
+ over the network can be slow, while copying the DB file locally is usually fast.
142
+
143
+ ## Convert Recording
144
+
145
+ ```python
146
+ from pathlib import Path
147
+
148
+ from twopy import convert_recording_to_twopy
149
+
150
+ recording = Path("/path/to/recording")
151
+
152
+ converted = convert_recording_to_twopy(recording)
153
+ print(converted.path)
154
+ print(converted.movie_path)
155
+ ```
156
+
157
+ Conversion writes `recording_data.h5` for metadata, stimulus tables,
158
+ photodiode signals, and the mean image. The large aligned movie is written
159
+ separately to `aligned_movie.h5`. By default the mean image uses the full movie;
160
+ pass `mean_start_frame` and `mean_stop_frame` to use a frame range. By default,
161
+ conversion writes to the location configured by `analysis_output`; pass
162
+ `output_dir` only when you want to override that for a specific call.
163
+
164
+ `config.yml` also controls analysis output routing. `analysis_output: source`
165
+ writes into `recording/twopy`; a path mirrors the recording directory structure
166
+ under that output root.
167
+
168
+ ## Analyze Converted Data
169
+
170
+ ```python
171
+ from pathlib import Path
172
+
173
+ import numpy as np
174
+
175
+ from twopy import (
176
+ classify_recording_photodiode_events,
177
+ compute_roi_delta_f_over_f,
178
+ detect_recording_photodiode_events,
179
+ extract_background_corrected_roi_traces,
180
+ load_converted_recording,
181
+ make_roi_set,
182
+ map_stimulus_epochs_to_frame_windows,
183
+ select_epoch_frame_windows,
184
+ )
185
+
186
+ recording = load_converted_recording(Path("/path/to/recording_data.h5"))
187
+ mask_array = np.zeros((1, *recording.movie.shape[1:]), dtype=bool)
188
+ mask_array[0, :10, :10] = True
189
+ roi_set = make_roi_set(mask_array)
190
+ traces = extract_background_corrected_roi_traces(
191
+ recording,
192
+ roi_set,
193
+ method="movie_global_percentile",
194
+ )
195
+ alignment = detect_recording_photodiode_events(recording)
196
+ timing = classify_recording_photodiode_events(recording, alignment)
197
+ epoch_windows = map_stimulus_epochs_to_frame_windows(recording, alignment)
198
+ interleave_windows = select_epoch_frame_windows(
199
+ epoch_windows,
200
+ epoch_name="Gray Interleave",
201
+ )
202
+ dff = compute_roi_delta_f_over_f(
203
+ traces,
204
+ interleave_windows,
205
+ data_rate_hz=float(recording.acquisition_metadata["acq.frameRate"]),
206
+ fit_mode="robust",
207
+ )
208
+ ```
209
+
210
+ Analysis starts from converted HDF5 files. ROI masks are GUI-independent, trace
211
+ extraction streams movie chunks, background correction stays explicit, and
212
+ stimulus epoch windows come from classified photodiode events instead of
213
+ nominal frame-rate assumptions. `timing.events` keeps the start, transition,
214
+ and end classifications auditable. Analysis trace extraction uses the saved
215
+ alignment-valid spatial crop by default, including `method="none"` when you
216
+ want uncorrected traces through the analysis path. ROI masks remain full-frame;
217
+ pass `spatial_domain="full_frame"` only when that is the intended audit path.
218
+ The lower-level `extract_roi_traces` helper is a full-frame raw primitive. ROI
219
+ dF/F uses corrected ROI fluorescence plus gray interleave windows to fit one
220
+ shared exponential tau and one amplitude per ROI. The default dF/F fit mode is
221
+ `robust`; pass `fit_mode="source_bounds"` when you need original source-bound
222
+ behavior for audit comparisons.
223
+
224
+ ## Open In Napari
225
+
226
+ From a converted output directory that contains `recording_data.h5`, or from a
227
+ source recording directory:
228
+
229
+ ```sh
230
+ twopy
231
+ ```
232
+
233
+ If converted files are missing and the selected folder has the expected source
234
+ recording files, twopy runs conversion first and then opens the converted HDF5
235
+ files. If no recording is found, `twopy` still opens napari. Choose a recording
236
+ folder or `recording_data.h5` in the `twopy` dock panel; twopy loads it after
237
+ selection.
238
+
239
+ Or pass a source folder or converted recording explicitly:
240
+
241
+ ```sh
242
+ twopy /path/to/source/recording
243
+ twopy /path/to/recording_data.h5
244
+ ```
245
+
246
+ By default the launcher opens the mean image, the full movie, an editable
247
+ `rois` Labels layer, a top response-plot dock, a `twopy` loading dock, and a left
248
+ Save ROIs dock. Use `--no-movie` to skip the movie preview, or `--movie-start`
249
+ and `--movie-end` to choose a different preview range. Save ROIs writes
250
+ `rois.h5` beside the current recording by default. The response dock can reload
251
+ existing `analysis_outputs.h5` or update plots from the current Labels layer.
252
+ Saving analysis writes `rois.h5`, `analysis_outputs.h5`,
253
+ `exports/csvs/response_summary_trials.csv`, and
254
+ `exports/csvs/response_summary_grouped.csv` beside the converted recording.
255
+ Response plots share one y-axis across epochs and show two seconds before
256
+ stimulus onset and two seconds after stimulus offset by default, when gray
257
+ interleave frames are available in the grouped responses. Epoch plots are laid
258
+ out horizontally. Each saved response trial includes its own `time_seconds`
259
+ vector, so plots use direct response time values rather than inferring time from
260
+ array indices.
261
+
262
+ ```python
263
+ from pathlib import Path
264
+
265
+ from twopy import (
266
+ launch_napari,
267
+ open_recording_in_napari,
268
+ roi_label_image_from_layer,
269
+ save_napari_label_rois,
270
+ )
271
+
272
+ launch_napari(Path("/path/to/recording_data.h5"))
273
+
274
+ view = open_recording_in_napari(
275
+ Path("/path/to/recording_data.h5"),
276
+ movie_frame_range=(0, 200),
277
+ )
278
+
279
+ # After drawing or editing the rois Labels layer:
280
+ label_image = roi_label_image_from_layer(view.roi_labels_layer)
281
+ roi_set = save_napari_label_rois(label_image, Path("/path/to/rois.h5"))
282
+ ```
283
+
284
+ Napari code is a thin adapter. It loads converted twopy files, displays the
285
+ mean image, optionally displays a bounded movie preview, creates an editable
286
+ ROI Labels layer, and adds small dock widgets for loading folders, saving ROIs,
287
+ and plotting responses. ROI saving writes the current Labels layer through the
288
+ core ROI HDF5 helpers. Response plotting calls the core analysis workflow when
289
+ updating from current ROIs. Pass `roi_set=Path("/path/to/rois.h5")` when
290
+ reopening existing ROIs. Napari code does not read source MATLAB/TIFF files or
291
+ own analysis decisions.
@@ -0,0 +1,67 @@
1
+ [build-system]
2
+ requires = ["setuptools>=77"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "twopy"
7
+ version = "0.1.0"
8
+ description = "Two-photon imaging analysis tool with a napari interface."
9
+ readme = "README.md"
10
+ requires-python = ">=3.13,<3.14"
11
+ license = "MIT"
12
+ authors = [
13
+ { name = "Gustavo Madeira Santana", email = "fromweb1@gumadeiras.com" },
14
+ ]
15
+ dependencies = [
16
+ "h5py>=3.16,<4",
17
+ "matplotlib>=3.10,<4",
18
+ "napari[all]>=0.7,<0.8",
19
+ "numpy>=2.3,<3",
20
+ "pre-commit>=4.6,<5",
21
+ "qtpy>=2.4,<3",
22
+ "pyyaml>=6.0.3,<7",
23
+ "ruff>=0.15",
24
+ "scipy>=1.17,<2",
25
+ "tifffile>=2026.3,<2027",
26
+ "ty>=0.0.34,<0.1",
27
+ ]
28
+
29
+ [project.scripts]
30
+ twopy = "twopy.napari:main"
31
+
32
+ [tool.setuptools.packages.find]
33
+ where = ["src"]
34
+
35
+ [tool.ruff]
36
+ line-length = 88
37
+ src = ["src", "tests"]
38
+ target-version = "py313"
39
+
40
+ [tool.ruff.lint]
41
+ select = [
42
+ "ANN",
43
+ "B",
44
+ "D",
45
+ "E",
46
+ "F",
47
+ "I",
48
+ "SIM",
49
+ "UP",
50
+ "W",
51
+ ]
52
+ ignore = [
53
+ "D203",
54
+ "D213",
55
+ ]
56
+
57
+ [tool.ruff.lint.pydocstyle]
58
+ convention = "google"
59
+
60
+ [tool.ty.environment]
61
+ python-version = "3.13"
62
+
63
+ [tool.ty.src]
64
+ include = ["src", "tests"]
65
+
66
+ [tool.ty.terminal]
67
+ error-on-warning = true
twopy-0.1.0/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+