Skip to content

Commit ff64304

Browse files
authored
Merge pull request #16 from ACRLab/ci-test
Add github actions and fix manual peaks
2 parents 3c73eb8 + 58bec97 commit ff64304

19 files changed

Lines changed: 448 additions & 231 deletions

.github/workflows/test.yaml

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
name: Test
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
branches:
9+
- main
10+
11+
jobs:
12+
test:
13+
runs-on: ${{ matrix.os }}
14+
strategy:
15+
fail-fast: false
16+
matrix:
17+
os: ["ubuntu-latest", "windows-latest", "macos-latest"]
18+
19+
steps:
20+
- name: Install Linux Qt deps
21+
if: runner.os == 'Linux'
22+
run: |
23+
sudo apt install \
24+
libgl1 \
25+
libegl1 \
26+
libdbus-1-3 \
27+
libxkbcommon-x11-0 \
28+
libxcb-icccm4 \
29+
libxcb-image0 \
30+
libxcb-keysyms1 \
31+
libxcb-randr0 \
32+
libxcb-render-util0 \
33+
libxcb-xinerama0 \
34+
libxcb-xfixes0 \
35+
x11-utils && \
36+
/sbin/start-stop-daemon --start --quiet \
37+
--pidfile /tmp/custom_xvfb_99.pid \
38+
--make-pidfile \
39+
--background \
40+
--exec /usr/bin/Xvfb -- \
41+
:99 -screen 0 1920x1200x24 -ac +extension GLX
42+
43+
- name: Checkout repository
44+
uses: actions/checkout@v4
45+
46+
- name: Set up Miniconda
47+
uses: conda-incubator/setup-miniconda@v3
48+
with:
49+
activate-environment: snazzy-env
50+
51+
- name: Install dependencies
52+
run: conda env update -f environment.yml -n snazzy-env --quiet
53+
54+
- name: Test with pytest
55+
run: conda run -n snazzy-env pytest
56+
env:
57+
QT_QPA_PLATFORM: offscreen
58+
MPLBACKEND: Agg
59+
HEADLESS: 1

environment.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,5 @@ dependencies:
3030
- statannotations==0.7.1
3131
- sphinx-book-theme==1.1.2
3232
- sphinx==7.3.7
33+
- -e ./snazzy_analysis
34+
- -e ./snazzy_processing

snazzy_analysis/snazzy_analysis/config.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ class PDParams(BaseModel):
5959

6060

6161
class EmbryoParams(BaseModel):
62-
wlen: int = 30
62+
wlen: int = 2
6363
manual_peaks: list[int] = Field(default_factory=list)
6464
manual_remove: list[int] = Field(default_factory=list)
6565
manual_widths: dict[str, Any] = Field(default_factory={})
@@ -175,7 +175,7 @@ def save_manual_peak_data(
175175

176176
if not emb_name in self.data["embryos"]:
177177
self.data["embryos"][emb_name] = {
178-
"wlen": 10,
178+
"wlen": 2,
179179
"manual_peaks": [],
180180
"manual_remove": [],
181181
"manual_widths": {},

snazzy_analysis/snazzy_analysis/gui/compare_plot_window.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datetime import datetime
2+
import os
23

34
import matplotlib
45
import matplotlib.axes
@@ -12,7 +13,10 @@
1213
from snazzy_analysis import FrequencyAnalysis, utils
1314
from snazzy_analysis.gui import GroupModel
1415

15-
matplotlib.use("QtAgg")
16+
if os.environ.get("CI") or os.environ.get("HEADLESS"):
17+
matplotlib.use("Agg")
18+
else:
19+
matplotlib.use("QtAgg")
1620

1721

1822
class ComparePlotWindow(QWidget):

snazzy_analysis/snazzy_analysis/gui/model.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def calc_peaks_all_embs(self, pd_params=None):
225225
{"pd_params": pd_params, "exp_params": {"to_remove": to_remove}}
226226
)
227227

228-
def add_peak(self, x, emb_name, trace, wlen=5):
228+
def add_peak(self, x, emb_name, trace, wlen=2):
229229
# load corrected data to reconcile with the new add
230230
exp = self.selected_experiment
231231
corrected_peaks = exp.config.get_corrected_peaks(emb_name)
@@ -246,31 +246,31 @@ def add_peak(self, x, emb_name, trace, wlen=5):
246246
)
247247
return new_peak, new_peaks
248248

249-
def remove_peak(self, x, emb_name, trace, wlen=10):
249+
def remove_peak(self, x, emb_name, trace, wlen=2):
250250
# load corrected data to reconcile with the new add
251251
exp = self.selected_experiment
252252
corrected_peaks = exp.config.get_corrected_peaks(emb_name)
253253
manual_add = [] if not corrected_peaks else corrected_peaks["manual_peaks"]
254254
manual_widths = (
255255
None if not corrected_peaks else corrected_peaks["manual_widths"]
256256
)
257+
wlen = wlen if not corrected_peaks else corrected_peaks["wlen"]
257258

258-
removed, new_arr, added_peaks, peak_width_to_remove = self.pm.remove_peak(
259-
x, trace, manual_add, manual_widths
259+
removed, new_arr, added_peaks, filtered_peak_widths = self.pm.remove_peak(
260+
x, trace, manual_add, manual_widths, wlen
260261
)
261262
# update corrected peaks
262263
if corrected_peaks:
263264
to_remove = corrected_peaks["manual_remove"]
264265
removed = list(set(to_remove + removed))
265266
corrected_peaks["manual_remove"] = removed
266-
if manual_widths and peak_width_to_remove:
267-
del manual_widths[str(peak_width_to_remove)]
267+
corrected_peaks["manual_widths"] = filtered_peak_widths
268268

269269
exp.config.save_manual_peak_data(
270270
emb_name,
271271
added_peaks=added_peaks,
272272
removed_peaks=removed,
273-
manual_widths=manual_widths,
273+
manual_widths=filtered_peak_widths,
274274
)
275275
return removed, new_arr
276276

snazzy_analysis/snazzy_analysis/gui/peak_matcher.py

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ def local_peak_at(x, signal, wlen):
1212

1313

1414
class PeakMatcher:
15-
def add_peak(self, x: int, trace: Trace, manual_remove: list[int], wlen: int = 5):
16-
"""Adds a new peak in the vicinity of `x`.
15+
def add_peak(self, x: int, trace: Trace, manual_remove: list[int], wlen: int = 2):
16+
"""Add a new peak in the vicinity of `x`.
1717
18-
The new peak is calculated as the local maximum near `x`, in a window of 2*wlen points.
18+
The new peak is calculated as the local maximum of a window centered at `x`,
19+
that ranges `wlen` in each direction.
1920
2021
Parameters:
2122
x (int):
@@ -37,19 +38,18 @@ def add_peak(self, x: int, trace: Trace, manual_remove: list[int], wlen: int = 5
3738
"""
3839
window = slice(x - wlen, x + wlen)
3940
new_peak = local_peak_at(x, trace.dff[window], wlen)
41+
if new_peak in trace.peak_idxes:
42+
return new_peak, trace.peak_idxes, None
4043
new_arr = np.append(trace.peak_idxes, new_peak)
4144
new_arr.sort()
4245

4346
# if we add a peak in a place where there's a manually removed peak,
4447
# that manually removed entry must be erased:
4548
to_remove = None
4649
if manual_remove:
47-
try:
48-
peak = next(p for p in manual_remove if x - wlen <= p <= x + wlen)
49-
i = manual_remove.index(peak)
50+
if new_peak in manual_remove:
51+
i = manual_remove.index(new_peak)
5052
to_remove = manual_remove[:i] + manual_remove[i + 1 :]
51-
except StopIteration:
52-
pass
5353

5454
return new_peak, new_arr, to_remove
5555

@@ -59,9 +59,9 @@ def remove_peak(
5959
trace: Trace,
6060
manual_add: list[int],
6161
manual_widths: dict[str, Any],
62-
wlen=10,
62+
wlen=2,
6363
):
64-
"""Removes peaks that are within `wlen` of `x`.
64+
"""Remove all peaks that are within ±`wlen` of `x`.
6565
6666
Parameters:
6767
x (int):
@@ -82,8 +82,8 @@ def remove_peak(
8282
Updated list with all peaks.
8383
to_add (list[int]):
8484
Updated list of manually added peaks.
85-
peak_width_to_remove (int):
86-
Key that should be removed from manual widths dict.
85+
filtered_peak_widths (int):
86+
Updated manual widhts dict without the peaks that were removed.
8787
"""
8888
target = (trace.peak_idxes >= x - wlen) & (trace.peak_idxes <= x + wlen)
8989
removed = trace.peak_idxes[target].tolist()
@@ -93,20 +93,17 @@ def remove_peak(
9393
# that manually added peak entry must be erased
9494
to_add = None
9595
if manual_add:
96-
try:
97-
# FIXME: if you remove more than one element, this update method fails
98-
peak = next(p for p in manual_add if x - wlen <= p <= x + wlen)
99-
i = manual_add.index(peak)
100-
to_add = manual_add[:i] + manual_add[i + 1 :]
101-
except StopIteration:
102-
pass
103-
peak_width_to_remove = None
96+
filtered_manual_add = [
97+
p for p in manual_add if p < x - wlen or p > x + wlen
98+
]
99+
to_add = filtered_manual_add
100+
101+
filtered_peak_widths = None
104102
if manual_widths:
105-
try:
106-
peak_width_to_remove = next(
107-
int(p) for p in manual_widths if x - wlen <= int(p) <= x + wlen
108-
)
109-
except StopIteration:
110-
pass
111-
112-
return removed, new_arr, to_add, peak_width_to_remove
103+
filtered_peak_widths = {
104+
p: data
105+
for p, data in manual_widths.items()
106+
if int(p) < x - wlen or int(p) > x + wlen
107+
}
108+
109+
return removed, new_arr, to_add, filtered_peak_widths

snazzy_analysis/snazzy_analysis/trace.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -344,21 +344,18 @@ def reconcile_manual_peaks(self, params):
344344
if corrected_peaks:
345345
to_add = corrected_peaks.get("manual_peaks", [])
346346
to_remove = corrected_peaks.get("manual_remove", [])
347-
wlen = corrected_peaks.get("wlen", 10)
347+
wlen = corrected_peaks.get("wlen", 2)
348+
348349
filtered_peaks = [
349350
p
350351
for p in self._peak_idxes
351352
if not any(abs(p - rp) < wlen for rp in to_remove)
352353
]
353-
filtered_add = [
354-
ap
355-
for ap in to_add
356-
if not any(abs(p - ap) < wlen for p in filtered_peaks)
357-
]
354+
358355
self.to_add = to_add
359356
self.to_remove = to_remove
360357
self._peak_idxes = np.array(
361-
sorted(filtered_peaks + filtered_add), dtype=np.int64
358+
sorted(list(set(filtered_peaks + to_add))), dtype=np.int64
362359
)
363360

364361
def update_dsna_start(self, params):

snazzy_analysis/tests/test_gui.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pathlib import Path
2+
import sys
23

34
import pytest
45
from PyQt6.QtCore import Qt
@@ -14,6 +15,10 @@
1415
)
1516
from snazzy_analysis.gui.model import GroupModel, ExperimentModel
1617

18+
pytestmark = pytest.mark.skipif(
19+
not sys.platform.startswith("linux"), reason="Running headless tests on linux only."
20+
)
21+
1722
VALID_DIR = Path(__file__).parent.joinpath("assets", "data", "20250210")
1823

1924

0 commit comments

Comments
 (0)