Skip to content

Commit b7377e2

Browse files
committed
Update SI-GUI from SpikeInterface
2 parents 4701db5 + f54beda commit b7377e2

File tree

3 files changed

+62
-4
lines changed

3 files changed

+62
-4
lines changed

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,9 @@ RUN wget "https://www.wavpack.com/wavpack-${WAVPACK_VERSION}.tar.bz2" && \
2323
RUN pip install wavpack-numcodecs
2424

2525
# Install spikeinterface-gui from source
26-
RUN git clone https://github.com/alejoe91/spikeinterface-gui.git && \
26+
RUN git clone https://github.com/SpikeInterface/spikeinterface-gui.git && \
2727
cd spikeinterface-gui && \
28-
git checkout a616ff653eea37632cac013bf21a39cdea18c0c1 && \
28+
git checkout e7c37434169df1ad107acdc2b7a6b4d3fd18821a && \
2929
pip install . && cd ..
3030

3131

src/aind_ephys_portal/panel/ephys_gui.py

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import sys
22
import param
33
import boto3
4+
import time
5+
from copy import deepcopy
46

57
import panel as pn
68

@@ -15,6 +17,19 @@
1517
from .utils import Tee
1618

1719

20+
displayed_unit_properties = ["decoder_label", "firing_rate", "y", "snr", "amplitude_median", "isi_violation_ratio"]
21+
default_curation_dict = {
22+
"label_definitions": {
23+
"quality":{
24+
"label_options": ["good", "MUA", "noise"],
25+
"exclusive": True,
26+
},
27+
},
28+
"manual_labels": [],
29+
"merge_unit_groups": [],
30+
"removed_units": [],
31+
}
32+
1833
class EphysGuiView(param.Parameterized):
1934

2035
def __init__(self, analyzer_path, recording_path, **params):
@@ -53,6 +68,7 @@ def __init__(self, analyzer_path, recording_path, **params):
5368

5469
def _initialize(self):
5570
if self.analyzer_input.value != "":
71+
t_start = time.perf_counter()
5672
spinner = pn.indicators.LoadingSpinner(value=True, sizing_mode="stretch_width")
5773
# Create a TextArea widget to display logs
5874
log_output = pn.widgets.TextAreaInput(value="", sizing_mode="stretch_both")
@@ -75,6 +91,8 @@ def _initialize(self):
7591
self.win = self._create_main_window()
7692
self.layout[1] = self.win
7793
print("Ephys GUI initialized successfully!")
94+
t_stop = time.perf_counter()
95+
print(f"Initialization time: {t_stop - t_start:.2f} seconds")
7896
sys.stdout = sys.__stdout__ # Reset stdout
7997
sys.stderr = sys.__stderr__ # Reset stderr
8098

@@ -115,10 +133,22 @@ def _check_if_s3_folder_exists(self, location):
115133

116134
def _create_main_window(self):
117135
if self.analyzer is not None:
136+
# prepare the curation data using decoder labels
137+
curation_dict = deepcopy(default_curation_dict)
138+
curation_dict["unit_ids"] = self.analyzer.unit_ids
139+
if "decoder_label" in self.analyzer.sorting.get_property_keys():
140+
decoder_labels = self.analyzer.get_sorting_property("decoder_label")
141+
noise_units = self.analyzer.unit_ids[decoder_labels == "noise"]
142+
curation_dict["removed_units"] = list(noise_units)
143+
for unit_id in noise_units:
144+
curation_dict["manual_labels"].append({"unit_id": unit_id, "quality": ["noise"]})
145+
118146
win = run_mainwindow(
119147
analyzer=self.analyzer,
120148
curation=True,
121149
skip_extensions=["waveforms"],
150+
displayed_unit_properties=displayed_unit_properties,
151+
curation_dict=curation_dict,
122152
backend="panel",
123153
start_app=False,
124154
make_servable=False,

src/aind_ephys_portal/panel/ephys_portal.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
"""Main Panel application for the AIND SIGUI Portal."""
22

33
import param
4-
import threading
54
import panel as pn
65
import pandas as pd
76
import boto3
7+
from botocore import UNSIGNED
8+
from botocore.config import Config
9+
810

911
from aind_ephys_portal.docdb.database import get_raw_asset_by_name, get_all_ecephys_derived
1012
from aind_ephys_portal.panel.utils import format_link, OUTER_STYLE, EPHYSGUI_LINK_PREFIX
@@ -113,7 +115,12 @@ def update_streams(self, event):
113115
links_url = []
114116
for stream_name in stream_names:
115117
raw_stream_name = stream_name[: stream_name.find("_recording")]
116-
recording_path = f"{raw_asset['location']}/ecephys/ecephys_compressed/{raw_stream_name}.zarr"
118+
raw_asset_prefix = self.get_raw_asset_location(raw_asset["location"])
119+
print(f"Raw asset prefix: {raw_asset_prefix}")
120+
if raw_asset_prefix is None:
121+
recording_path=""
122+
else:
123+
recording_path = f"{raw_asset_prefix}/{raw_stream_name}.zarr"
117124
analyzer_path = f"{analyzer_base_location}/postprocessed/{stream_name}"
118125
print("Raw path:", recording_path)
119126
print("Analyzer path:", analyzer_path)
@@ -140,6 +147,27 @@ def update_streams(self, event):
140147
# # Schedule next run in 1 hour (3600 seconds)
141148
# threading.Timer(3600, self.auto_update_datasets).start()
142149

150+
def get_raw_asset_location(self, asset_location):
151+
asset_without_s3 = asset_location[asset_location.find("s3://") + 5:]
152+
asset_split = asset_without_s3.split("/")
153+
bucket_name = asset_split[0]
154+
session_name = "/".join(asset_split[1:])
155+
possible_locations = ["ecephys/ecephys_compressed", "ecephys_compressed"]
156+
raw_asset_location = None
157+
for location in possible_locations:
158+
prefix = f"{session_name}/{location}/"
159+
try:
160+
response = s3_client.list_objects_v2(Bucket=bucket_name, Prefix=prefix, MaxKeys=1)
161+
except Exception as e:
162+
print(f"Error listing objects with unsigned client from {bucket_name}/{prefix}: {e}")
163+
continue
164+
if "Contents" in response:
165+
raw_asset_location = f"s3://{bucket_name}/{prefix}"
166+
break
167+
if raw_asset_location is not None and raw_asset_location.endswith("/"):
168+
raw_asset_location = raw_asset_location[:-1]
169+
return raw_asset_location
170+
143171
def panel(self):
144172
"""Build a Panel object representing the Ephys Portal."""
145173
# Create a layout with the search bar at the top, results panel in the middle,

0 commit comments

Comments
 (0)