Skip to content

Commit f56aba0

Browse files
authored
Misc fixes (#1192)
* #1175 * #1185 * #1183 * Fix circular import * #1163 * #1105 * Fix failing tests, close download subprocesses * WIP: fix decode changes spikesort tests * Fix fickle test * Revert typo
1 parent 6faed4c commit f56aba0

File tree

15 files changed

+162
-84
lines changed

15 files changed

+162
-84
lines changed

CHANGELOG.md

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ dj.FreeTable(dj.conn(), "common_session.session_group").drop()
4343
- Remove numpy version restriction #1169
4444
- Merge table delete removes orphaned master entries #1164
4545
- Edit `merge_fetch` to expect positional before keyword arguments #1181
46+
- Allow part restriction `SpyglassMixinPart.delete` #1192
4647

4748
### Pipelines
4849

@@ -52,8 +53,11 @@ dj.FreeTable(dj.conn(), "common_session.session_group").drop()
5253
- Improve electrodes import efficiency #1125
5354
- Fix logger method call in `common_task` #1132
5455
- Export fixes #1164
55-
- Allow `get_abs_path` to add selection entry.
56-
- Log restrictions and joins.
56+
- Allow `get_abs_path` to add selection entry. #1164
57+
- Log restrictions and joins. #1164
58+
- Check if querying table inherits mixin in `fetch_nwb`. #1192
59+
- Ensure externals entries before adding to export. #1192
60+
- Error specificity in `LabMemberInfo` #1192
5761

5862
- Decoding
5963

@@ -74,6 +78,7 @@ dj.FreeTable(dj.conn(), "common_session.session_group").drop()
7478
`open-cv` #1168
7579
- `VideoMaker` class to process frames in multithreaded batches #1168, #1174
7680
- `TrodesPosVideo` updates for `matplotlib` processor #1174
81+
- User prompt if ambiguous insert in `DLCModelSource` #1192
7782

7883
- Spike Sorting
7984

pyproject.toml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ addopts = [
131131
# "--pdb", # drop into debugger on failure
132132
"-p no:warnings",
133133
# "--no-teardown", # don't teardown the database after tests
134-
# "--quiet-spy", # don't show logging from spyglass
134+
"--quiet-spy", # don't show logging from spyglass
135135
# "--no-dlc", # don't run DLC tests
136136
"--show-capture=no",
137137
"--pdbcls=IPython.terminal.debugger:TerminalPdb", # use ipython debugger
@@ -148,6 +148,12 @@ env = [
148148
"TF_ENABLE_ONEDNN_OPTS = 0", # TF disable approx calcs
149149
"TF_CPP_MIN_LOG_LEVEL = 2", # Disable TF warnings
150150
]
151+
filterwarnings = [
152+
"ignore::ResourceWarning:.*",
153+
"ignore::DeprecationWarning:.*",
154+
"ignore::UserWarning:.*",
155+
"ignore::MissingRequiredBuildWarning:.*",
156+
]
151157

152158
[tool.coverage.run]
153159
source = ["*/src/spyglass/*"]

src/spyglass/common/common_lab.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,11 @@ def get_djuser_name(cls, dj_user) -> str:
133133
)
134134

135135
if len(query) != 1:
136+
remedy = f"delete {len(query)-1}" if len(query) > 1 else "add one"
136137
raise ValueError(
137-
f"Could not find name for datajoint user {dj_user}"
138-
+ f" in common.LabMember.LabMemberInfo: {query}"
138+
f"Could not find exactly 1 datajoint user {dj_user}"
139+
+ " in common.LabMember.LabMemberInfo. "
140+
+ f"Please {remedy}: {query}"
139141
)
140142

141143
return query[0]

src/spyglass/common/common_usage.py

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,10 @@
99
from typing import List, Union
1010

1111
import datajoint as dj
12-
from datajoint import FreeTable
13-
from datajoint import config as dj_config
1412
from pynwb import NWBHDF5IO
1513

1614
from spyglass.common.common_nwbfile import AnalysisNwbfile, Nwbfile
17-
from spyglass.settings import export_dir, test_mode
15+
from spyglass.settings import test_mode
1816
from spyglass.utils import SpyglassMixin, SpyglassMixinPart, logger
1917
from spyglass.utils.dj_graph import RestrGraph
2018
from spyglass.utils.dj_helper_fn import (
@@ -174,7 +172,6 @@ def list_file_paths(self, key: dict, as_dict=True) -> list[str]:
174172
Return as a list of dicts: [{'file_path': x}]. Default True.
175173
If False, returns a list of strings without key.
176174
"""
177-
file_table = self * self.File & key
178175
unique_fp = {
179176
*[
180177
AnalysisNwbfile().get_abs_path(p)
@@ -210,21 +207,26 @@ def _add_externals_to_restr_graph(
210207
restr_graph : RestrGraph
211208
The updated RestrGraph
212209
"""
213-
raw_tbl = self._externals["raw"]
214-
raw_name = raw_tbl.full_table_name
215-
raw_restr = (
216-
"filepath in ('" + "','".join(self._list_raw_files(key)) + "')"
217-
)
218-
restr_graph.graph.add_node(raw_name, ft=raw_tbl, restr=raw_restr)
219-
220-
analysis_tbl = self._externals["analysis"]
221-
analysis_name = analysis_tbl.full_table_name
222-
analysis_restr = ( # filepaths have analysis subdir. regexp substrings
223-
"filepath REGEXP '" + "|".join(self._list_analysis_files(key)) + "'"
224-
) # regexp is slow, but we're only doing this once, and future-proof
225-
restr_graph.graph.add_node(
226-
analysis_name, ft=analysis_tbl, restr=analysis_restr
227-
)
210+
211+
if raw_files := self._list_raw_files(key):
212+
raw_tbl = self._externals["raw"]
213+
raw_name = raw_tbl.full_table_name
214+
raw_restr = "filepath in ('" + "','".join(raw_files) + "')"
215+
restr_graph.graph.add_node(raw_name, ft=raw_tbl, restr=raw_restr)
216+
restr_graph.visited.add(raw_name)
217+
218+
if analysis_files := self._list_analysis_files(key):
219+
analysis_tbl = self._externals["analysis"]
220+
analysis_name = analysis_tbl.full_table_name
221+
# to avoid issues with analysis subdir, we use REGEXP
222+
# this is slow, but we're only doing this once, and future-proof
223+
analysis_restr = (
224+
"filepath REGEXP '" + "|".join(analysis_files) + "'"
225+
)
226+
restr_graph.graph.add_node(
227+
analysis_name, ft=analysis_tbl, restr=analysis_restr
228+
)
229+
restr_graph.visited.add(analysis_name)
228230

229231
restr_graph.visited.update({raw_name, analysis_name})
230232

src/spyglass/decoding/v1/core.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,8 @@ def create_group(
126126
}
127127
if self & group_key:
128128
logger.error( # Easier for pytests to not raise error on duplicate
129-
f"Group {nwb_file_name}: {group_name} already exists"
130-
+ "please delete the group before creating a new one"
129+
f"Group {nwb_file_name}: {group_name} already exists. "
130+
+ "Please delete the group before creating a new one"
131131
)
132132
return
133133
self.insert1(

src/spyglass/position/v1/position_dlc_model.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,16 +98,24 @@ def insert_entry(
9898
dj.conn(), full_table_name=part_table.parents()[-1]
9999
) & {"project_name": project_name}
100100

101-
if cls._test_mode: # temporary fix for #1105
102-
project_path = table_query.fetch(limit=1)[0]
103-
else:
104-
project_path = table_query.fetch1("project_path")
101+
n_found = len(table_query)
102+
if n_found != 1:
103+
logger.warning(
104+
f"Found {len(table_query)} entries found for project "
105+
+ f"{project_name}:\n{table_query}"
106+
)
107+
108+
choice = "y"
109+
if n_found > 1 and not cls._test_mode:
110+
choice = dj.utils.user_choice("Use first entry?")[0]
111+
if n_found == 0 or choice != "y":
112+
return
105113

106114
part_table.insert1(
107115
{
108116
"dlc_model_name": dlc_model_name,
109117
"project_name": project_name,
110-
"project_path": project_path,
118+
"project_path": table_query.fetch("project_path", limit=1)[0],
111119
**key,
112120
},
113121
**kwargs,

src/spyglass/position/v1/position_dlc_project.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,6 @@ class DLCProject(SpyglassMixin, dj.Manual):
5757
With ability to edit config, extract frames, label frames
5858
"""
5959

60-
# Add more parameters as secondary keys...
61-
# TODO: collapse params into blob dict
6260
definition = """
6361
project_name : varchar(100) # name of DLC project
6462
---

src/spyglass/spikesorting/v1/curation.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99
import spikeinterface.extractors as se
1010

1111
from spyglass.common import BrainRegion, Electrode
12-
from spyglass.common.common_ephys import Raw
1312
from spyglass.common.common_nwbfile import AnalysisNwbfile
1413
from spyglass.spikesorting.v1.recording import (
1514
SortGroup,
1615
SpikeSortingRecording,
1716
SpikeSortingRecordingSelection,
1817
)
1918
from spyglass.spikesorting.v1.sorting import SpikeSorting, SpikeSortingSelection
20-
from spyglass.utils.dj_mixin import SpyglassMixin
19+
from spyglass.utils import SpyglassMixin, logger
2120

2221
schema = dj.schema("spikesorting_v1_curation")
2322

@@ -84,13 +83,13 @@ def insert_curation(
8483

8584
sort_query = cls & {"sorting_id": sorting_id}
8685
parent_curation_id = max(parent_curation_id, -1)
87-
if parent_curation_id == -1:
86+
87+
parent_query = sort_query & {"curation_id": parent_curation_id}
88+
if parent_curation_id == -1 and len(parent_query):
8889
# check to see if this sorting with a parent of -1
8990
# has already been inserted and if so, warn the user
90-
query = sort_query & {"parent_curation_id": -1}
91-
if query:
92-
Warning("Sorting has already been inserted.")
93-
return query.fetch("KEY")
91+
logger.warning("Sorting has already been inserted.")
92+
return parent_query.fetch("KEY")
9493

9594
# generate curation ID
9695
existing_curation_ids = sort_query.fetch("curation_id")

src/spyglass/utils/dj_helper_fn.py

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ def get_nwb_table(query_expression, tbl, attr_name, *attrs, **kwargs):
223223
Function to get the absolute path to the NWB file.
224224
"""
225225
from spyglass.common.common_nwbfile import AnalysisNwbfile, Nwbfile
226+
from spyglass.utils.dj_mixin import SpyglassMixin
226227

227228
kwargs["as_dict"] = True # force return as dictionary
228229
attrs = attrs or query_expression.heading.names # if none, all
@@ -234,11 +235,18 @@ def get_nwb_table(query_expression, tbl, attr_name, *attrs, **kwargs):
234235
}
235236
file_name_str, file_path_fn = tbl_map[which]
236237

238+
# logging arg only if instanced table inherits Mixin
239+
inst = ( # instancing may not be necessary
240+
query_expression()
241+
if isinstance(query_expression, type)
242+
and issubclass(query_expression, dj.Table)
243+
else query_expression
244+
)
245+
arg = dict(log_export=False) if isinstance(inst, SpyglassMixin) else dict()
246+
237247
# TODO: check that the query_expression restricts tbl - CBroz
238248
nwb_files = (
239-
query_expression.join(
240-
tbl.proj(nwb2load_filepath=attr_name), log_export=False
241-
)
249+
query_expression.join(tbl.proj(nwb2load_filepath=attr_name), **arg)
242250
).fetch(file_name_str)
243251

244252
# Disabled #1024

src/spyglass/utils/dj_mixin.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -145,10 +145,10 @@ def _nwb_table_tuple(self) -> tuple:
145145
146146
Used to determine fetch_nwb behavior. Also used in Merge.fetch_nwb.
147147
Implemented as a cached_property to avoid circular imports."""
148-
from spyglass.common.common_nwbfile import (
148+
from spyglass.common.common_nwbfile import ( # noqa F401
149149
AnalysisNwbfile,
150150
Nwbfile,
151-
) # noqa F401
151+
)
152152

153153
table_dict = {
154154
AnalysisNwbfile: "analysis_file_abs_path",
@@ -857,4 +857,9 @@ def delete(self, *args, **kwargs):
857857
"""Delete master and part entries."""
858858
restriction = self.restriction or True # for (tbl & restr).delete()
859859

860-
(self.master & restriction).delete(*args, **kwargs)
860+
try: # try restriction on master
861+
restricted = self.master & restriction
862+
except DataJointError: # if error, assume restr of self
863+
restricted = self & restriction
864+
865+
restricted.delete(*args, **kwargs)

0 commit comments

Comments
 (0)