Skip to content
This repository was archived by the owner on Dec 8, 2023. It is now read-only.

Commit 26a8bf6

Browse files
author
Thinh Nguyen
authored
Merge pull request #50 from kabilar/main
Add NWB export pytests
2 parents f7b9c97 + c470d1e commit 26a8bf6

File tree

8 files changed

+370
-33
lines changed

8 files changed

+370
-33
lines changed

README.md

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
# DataJoint Workflow - Array Electrophysiology
22

3-
Workflow for extracellular array electrophysiology data acquired with a polytrode probe (e.g.
4-
Neuropixels, Neuralynx) using the `SpikeGLX` or `OpenEphys` acquisition software and processed
5-
with MATLAB- or python-based `Kilosort` spike sorting software.
3+
Workflow for extracellular array electrophysiology data acquired with a polytrode probe
4+
(e.g. [Neuropixels](https://www.neuropixels.org), Neuralynx) using the [SpikeGLX](https://github.com/billkarsh/SpikeGLX) or
5+
[OpenEphys](https://open-ephys.org/gui) acquisition software and processed with [MATLAB-based Kilosort](https://github.com/MouseLand/Kilosort) or [python-based Kilosort](https://github.com/MouseLand/pykilosort) spike sorting software.
66

77
A complete electrophysiology workflow can be built using the DataJoint Elements.
88
+ [element-lab](https://github.com/datajoint/element-lab)
@@ -18,34 +18,43 @@ convention, and directory lookup methods (see
1818
[workflow_array_ephys/paths.py](workflow_array_ephys/paths.py)).
1919
3. Ingestion of clustering results.
2020

21-
## Workflow architecture
22-
23-
The electrophysiology workflow presented here uses components from 4 DataJoint
24-
Elements (`element-lab`, `element-animal`, `element-session`,
25-
`element-array-ephys`) assembled together to form a fully functional workflow.
26-
27-
### element-lab
21+
See the [Element Array Electrophysiology documentation](https://elements.datajoint.org/description/array_ephys/) for the background information and development timeline.
2822

29-
![element-lab](
30-
https://github.com/datajoint/element-lab/raw/main/images/lab_diagram.svg)
23+
For more information on the DataJoint Elements project, please visit https://elements.datajoint.org. This work is supported by the National Institutes of Health.
3124

32-
### element-animal
33-
34-
![element-animal](
35-
https://github.com/datajoint/element-animal/raw/main/images/subject_diagram.svg)
25+
## Workflow architecture
3626

37-
### assembled with element-array-ephys
27+
The electrophysiology workflow presented here uses components from 4 DataJoint
28+
Elements ([element-lab](https://github.com/datajoint/element-lab),
29+
[element-animal](https://github.com/datajoint/element-animal),
30+
[element-session](https://github.com/datajoint/element-session),
31+
[element-array-ephys](https://github.com/datajoint/element-array-ephys))
32+
assembled together to form a fully functional workflow.
3833

3934
![element-array-ephys](images/attached_array_ephys_element.svg)
4035

4136
## Installation instructions
4237

4338
+ The installation instructions can be found at the
44-
[datajoint-elements repository](https://github.com/datajoint/datajoint-elements/blob/main/gh-pages/docs/install.md).
39+
[DataJoint Elements documentation](https://elements.datajoint.org/usage/install/).
4540

4641
## Interacting with the DataJoint workflow
4742

4843
+ Please refer to the following workflow-specific
4944
[Jupyter notebooks](/notebooks) for an in-depth explanation of how to run the
5045
workflow ([03-process.ipynb](notebooks/03-process.ipynb)) and explore the data
5146
([05-explore.ipynb](notebooks/05-explore.ipynb)).
47+
48+
## Citation
49+
50+
+ If your work uses DataJoint and DataJoint Elements, please cite the respective Research Resource Identifiers (RRIDs) and manuscripts.
51+
52+
+ DataJoint for Python or MATLAB
53+
+ Yatsenko D, Reimer J, Ecker AS, Walker EY, Sinz F, Berens P, Hoenselaar A, Cotton RJ, Siapas AS, Tolias AS. DataJoint: managing big scientific data using MATLAB or Python. bioRxiv. 2015 Jan 1:031658. doi: https://doi.org/10.1101/031658
54+
55+
+ DataJoint ([RRID:SCR_014543](https://scicrunch.org/resolver/SCR_014543)) - DataJoint for `<Select Python or MATLAB>` (version `<Enter version number>`)
56+
57+
+ DataJoint Elements
58+
+ Yatsenko D, Nguyen T, Shen S, Gunalan K, Turner CA, Guzman R, Sasaki M, Sitonic D, Reimer J, Walker EY, Tolias AS. DataJoint Elements: Data Workflows for Neurophysiology. bioRxiv. 2021 Jan 1. doi: https://doi.org/10.1101/2021.03.30.437358
59+
60+
+ DataJoint Elements ([RRID:SCR_021894](https://scicrunch.org/resolver/SCR_021894)) - Element Array Electrophysiology (version `<Enter version number>`)

docker/Dockerfile.dev

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,15 @@ RUN mkdir /main/element-lab \
99
/main/element-animal \
1010
/main/element-session \
1111
/main/element-array-ephys \
12+
/main/element-interface \
1213
/main/workflow-array-ephys
1314

1415
# Copy user's local fork of elements and workflow
1516
COPY --chown=anaconda:anaconda ./element-lab /main/element-lab
1617
COPY --chown=anaconda:anaconda ./element-animal /main/element-animal
1718
COPY --chown=anaconda:anaconda ./element-session /main/element-session
1819
COPY --chown=anaconda:anaconda ./element-array-ephys /main/element-array-ephys
20+
COPY --chown=anaconda:anaconda ./element-interface /main/element-interface
1921
COPY --chown=anaconda:anaconda ./workflow-array-ephys /main/workflow-array-ephys
2022

2123
# Install packages
@@ -24,6 +26,7 @@ RUN pip install -e /main/element-animal
2426
RUN pip install -e /main/element-session
2527
RUN pip install -e /main/element-array-ephys
2628
RUN pip install -e /main/workflow-array-ephys
29+
RUN pip install -e /main/element-interface
2730
RUN pip install -r /main/workflow-array-ephys/requirements_test.txt
2831

2932
WORKDIR /main/workflow-array-ephys

docker/docker-compose-dev.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ services:
2929
- ../../element-animal:/main/element-animal
3030
- ../../element-session:/main/element-session
3131
- ../../element-array-ephys:/main/element-array-ephys
32+
- ../../element-interface:/main/element-interface
3233
- ..:/main/workflow-array-ephys
3334
depends_on:
3435
db:

tests/__init__.py

Lines changed: 168 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
_tear_down = True
2121
verbose = False
2222

23-
test_user_data_dir = pathlib.Path('./tests/user_data')
24-
test_user_data_dir.mkdir(exist_ok=True)
23+
pathlib.Path('./tests/user_data').mkdir(exist_ok=True)
24+
pathlib.Path('./tests/user_data/lab').mkdir(exist_ok=True)
2525

2626
sessions_dirs = ['subject1/session1',
2727
'subject2/session1',
@@ -34,6 +34,17 @@
3434
# -------------------- HELPER CLASS --------------------
3535

3636

37+
def write_csv(content, path):
38+
"""
39+
General function for writing strings to lines in CSV
40+
:param path: pathlib PosixPath
41+
:param content: list of strings, each as row of CSV
42+
"""
43+
with open(path, 'w') as f:
44+
for line in content:
45+
f.write(line+'\n')
46+
47+
3748
class QuietStdOut:
3849
"""If verbose set to false, used to quiet tear_down table.delete prints"""
3950
def __enter__(self):
@@ -121,6 +132,147 @@ def pipeline():
121132
pipeline.subject.Subject.delete()
122133

123134

135+
@pytest.fixture
136+
def lab_csv():
137+
""" Create a 'labs.csv' file"""
138+
lab_content = ["lab,lab_name,institution,address,"
139+
+ "time_zone,location,location_description",
140+
"LabA,The Example Lab,Example Uni,"
141+
+ "'221B Baker St,London NW1 6XE,UK',UTC+0,"
142+
+ "Example Building,'2nd floor lab dedicated to all "
143+
+ "fictional experiments.'",
144+
"LabB,The Other Lab,Other Uni,"
145+
+ "'Oxford OX1 2JD, United Kingdom',UTC+0,"
146+
+ "Other Building,'fictional campus dedicated to imaginary"
147+
+ "experiments.'"]
148+
lab_csv_path = pathlib.Path('./tests/user_data/lab/labs.csv')
149+
write_csv(lab_content, lab_csv_path)
150+
151+
yield lab_content, lab_csv_path
152+
lab_csv_path.unlink()
153+
154+
155+
@pytest.fixture
156+
def lab_project_csv():
157+
""" Create a 'projects.csv' file"""
158+
lab_project_content = ["project,project_description,repository_url,"
159+
+ "repository_name,codeurl",
160+
"ProjA,Example project to populate element-lab,"
161+
+ "https://github.com/datajoint/element-lab/,"
162+
+ "element-lab,https://github.com/datajoint/element"
163+
+ "-lab/tree/main/element_lab",
164+
"ProjB,Other example project to populate element-"
165+
+ "lab,https://github.com/datajoint/element-session"
166+
+ "/,element-session,https://github.com/datajoint/"
167+
+ "element-session/tree/main/element_session"]
168+
lab_project_csv_path = pathlib.Path('./tests/user_data/lab/projects.csv')
169+
write_csv(lab_project_content, lab_project_csv_path)
170+
171+
yield lab_project_content, lab_project_csv_path
172+
lab_project_csv_path.unlink()
173+
174+
175+
@pytest.fixture
176+
def lab_project_users_csv():
177+
""" Create a 'project_users.csv' file"""
178+
lab_project_user_content = ["user,project",
179+
"Sherlock,ProjA",
180+
"Sherlock,ProjB",
181+
"Watson,ProjB",
182+
"Dr. Candace Pert,ProjA",
183+
"User1,ProjA"]
184+
lab_project_user_csv_path = pathlib.Path('./tests/user_data/lab/\
185+
project_users.csv')
186+
write_csv(lab_project_user_content, lab_project_user_csv_path)
187+
188+
yield lab_project_user_content, lab_project_user_csv_path
189+
lab_project_user_csv_path.unlink()
190+
191+
192+
@pytest.fixture
193+
def lab_publications_csv():
194+
""" Create a 'publications.csv' file"""
195+
lab_publication_content = ["project,publication",
196+
"ProjA,arXiv:1807.11104",
197+
"ProjA,arXiv:1807.11104v1"]
198+
lab_publication_csv_path = pathlib.Path('./tests/user_data/lab/\
199+
publications.csv')
200+
write_csv(lab_publication_content, lab_publication_csv_path)
201+
202+
yield lab_publication_content, lab_publication_csv_path
203+
lab_publication_csv_path.unlink()
204+
205+
206+
@pytest.fixture
207+
def lab_keywords_csv():
208+
""" Create a 'keywords.csv' file"""
209+
lab_keyword_content = ["project,keyword",
210+
"ProjA,Study",
211+
"ProjA,Example",
212+
"ProjB,Alternate"]
213+
lab_keyword_csv_path = pathlib.Path('./tests/user_data/lab/keywords.csv')
214+
write_csv(lab_keyword_content, lab_keyword_csv_path)
215+
216+
yield lab_keyword_content, lab_keyword_csv_path
217+
lab_keyword_csv_path.unlink()
218+
219+
220+
@pytest.fixture
221+
def lab_protocol_csv():
222+
""" Create a 'protocols.csv' file"""
223+
lab_protocol_content = ["protocol,protocol_type,protocol_description",
224+
"ProtA,IRB expedited review,Protocol for managing "
225+
+ "data ingestion",
226+
"ProtB,Alternative Method,Limited protocol for "
227+
+ "piloting only"]
228+
lab_protocol_csv_path = pathlib.Path('./tests/user_data/lab/protocols.csv')
229+
write_csv(lab_protocol_content, lab_protocol_csv_path)
230+
231+
yield lab_protocol_content, lab_protocol_csv_path
232+
lab_protocol_csv_path.unlink()
233+
234+
235+
@pytest.fixture
236+
def lab_user_csv():
237+
""" Create a 'users.csv' file"""
238+
lab_user_content = ["lab,user,user_role,user_email,user_cellphone",
239+
"LabA,Sherlock,PI,Sherlock@BakerSt.com,"
240+
+ "+44 20 7946 0344",
241+
"LabA,Watson,Dr,DrWatson@BakerSt.com,+44 73 8389 1763",
242+
"LabB,Dr. Candace Pert,PI,Pert@gmail.com,"
243+
+ "+44 74 4046 5899",
244+
"LabA,User1,Lab Tech,fake@email.com,+44 1632 960103",
245+
"LabB,User2,Lab Tech,fake2@email.com,+44 1632 960102"]
246+
lab_user_csv_path = pathlib.Path('./tests/user_data/lab/users.csv')
247+
write_csv(lab_user_content, lab_user_csv_path)
248+
249+
yield lab_user_content, lab_user_csv_path
250+
lab_user_csv_path.unlink()
251+
252+
253+
@pytest.fixture
254+
def ingest_lab(pipeline, lab_csv, lab_project_csv, lab_publications_csv,
255+
lab_keywords_csv, lab_protocol_csv, lab_user_csv,
256+
lab_project_users_csv):
257+
""" From workflow_array_ephys ingest.py, import ingest_lab, run """
258+
from workflow_array_ephys.ingest import ingest_lab
259+
_, lab_csv_path = lab_csv
260+
_, lab_project_csv_path = lab_project_csv
261+
_, lab_publication_csv_path = lab_publications_csv
262+
_, lab_keyword_csv_path = lab_keywords_csv
263+
_, lab_protocol_csv_path = lab_protocol_csv
264+
_, lab_user_csv_path = lab_user_csv
265+
_, lab_project_user_csv_path = lab_project_users_csv
266+
ingest_lab(lab_csv_path=lab_csv_path,
267+
project_csv_path=lab_project_csv_path,
268+
publication_csv_path=lab_publication_csv_path,
269+
keyword_csv_path=lab_keyword_csv_path,
270+
protocol_csv_path=lab_protocol_csv_path,
271+
users_csv_path=lab_user_csv_path,
272+
project_user_csv_path=lab_project_user_csv_path, verbose=verbose)
273+
return
274+
275+
124276
@pytest.fixture
125277
def subjects_csv():
126278
""" Create a 'subjects.csv' file"""
@@ -160,11 +312,23 @@ def ingest_subjects(pipeline, subjects_csv):
160312
@pytest.fixture
161313
def sessions_csv(test_data):
162314
""" Create a 'sessions.csv' file"""
163-
input_sessions = pd.DataFrame(columns=['subject', 'session_dir'])
315+
input_sessions = pd.DataFrame(columns=['subject', 'session_dir', 'session_note',
316+
'user'])
164317
input_sessions.subject = ['subject1', 'subject2', 'subject2',
165318
'subject3', 'subject4', 'subject5',
166319
'subject6']
167320
input_sessions.session_dir = sessions_dirs
321+
input_sessions.session_note = ['Data collection notes',
322+
'Data collection notes',
323+
'Interrupted session',
324+
'Data collection notes',
325+
'Successful data collection',
326+
'Successful data collection',
327+
'Ambient temp abnormally low']
328+
input_sessions.user = ['User2', 'User2', 'User2',
329+
'User1', 'User2', 'User1',
330+
'User2']
331+
168332
input_sessions = input_sessions.set_index('subject')
169333

170334
sessions_csv_path = pathlib.Path('./tests/user_data/sessions.csv')
@@ -211,7 +375,7 @@ def ephys_insertionlocation(pipeline, ingest_sessions):
211375
depth=0,
212376
theta=0,
213377
phi=0,
214-
beta=0))
378+
beta=0), skip_duplicates=True)
215379
yield
216380

217381
if _tear_down:

0 commit comments

Comments
 (0)