Skip to content

Commit 664f051

Browse files
authored
Log when file accessed (#941)
* Add logging for any func that creates AnalysisNwbfile
1 parent 6763d79 commit 664f051

File tree

11 files changed

+93
-37
lines changed

11 files changed

+93
-37
lines changed

CHANGELOG.md

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,6 @@
11
# Change Log
22

3-
## [0.5.2] (Unreleased)
4-
5-
### Release Notes
6-
7-
<!-- Running draft to be removed immediately prior to release. -->
3+
## [0.5.2] (April 22, 2024)
84

95
### Infrastructure
106

@@ -20,14 +16,15 @@
2016
- Prioritize datajoint filepath entry for defining abs_path of analysis nwbfile
2117
#918
2218
- Fix potential duplicate entries in Merge part tables #922
23-
- Add logging of AnalysisNwbfile creation time and size #937
19+
- Add log of AnalysisNwbfile creation time, size, and access count #937, #941
2420

2521
### Pipelines
2622

2723
- Spikesorting
2824
- Update calls in v0 pipeline for spikeinterface>=0.99 #893
2925
- Fix method type of `get_spike_times` #904
30-
- Add helper functions for restricting spikesorting results and linking to probe info #910
26+
- Add helper functions for restricting spikesorting results and linking to
27+
probe info #910
3128
- Decoding
3229
- Handle dimensions of clusterless `get_ahead_behind_distance` #904
3330
- Fix improper handling of nwb file names with .strip #929

CITATION.cff

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,5 +166,5 @@ keywords:
166166
- spike sorting
167167
- kachery
168168
license: MIT
169-
version: 0.5.1
170-
date-released: '2024-03-07'
169+
version: 0.5.2
170+
date-released: '2024-04-22'

src/spyglass/common/common_nwbfile.py

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -693,21 +693,37 @@ def log(self, analysis_file_name, table=None):
693693
table=table,
694694
)
695695

696+
def increment_access(self, keys, table=None):
697+
"""Passthrough to the AnalysisNwbfileLog table. Avoid new imports."""
698+
if not isinstance(keys, list):
699+
key = [keys]
700+
701+
for key in keys:
702+
AnalysisNwbfileLog().increment_access(key, table=table)
703+
696704

697705
@schema
698706
class AnalysisNwbfileLog(dj.Manual):
699707
definition = """
700708
id: int auto_increment
701709
---
702-
dj_user: varchar(64)
703710
-> AnalysisNwbfile
704-
table=null: varchar(64)
705-
timestamp = CURRENT_TIMESTAMP : timestamp
706-
time_delta=null: float
707-
file_size=null: float
711+
dj_user : varchar(64) # user who created the file
712+
timestamp = CURRENT_TIMESTAMP : timestamp # when the file was created
713+
table = null : varchar(64) # creating table
714+
time_delta = null : float # how long it took to create
715+
file_size = null : float # size of the file in bytes
716+
accessed = 0 : int # n times accessed
717+
unique index (analysis_file_name)
708718
"""
709719

710-
def log(self, analysis_file_name, time_delta, file_size, table=None):
720+
def log(
721+
self,
722+
analysis_file_name=None,
723+
time_delta=None,
724+
file_size=None,
725+
table=None,
726+
):
711727
"""Log the creation of an analysis NWB file.
712728
713729
Parameters
@@ -724,3 +740,29 @@ def log(self, analysis_file_name, time_delta, file_size, table=None):
724740
"table": table,
725741
}
726742
)
743+
744+
def increment_access(self, key, table=None):
745+
"""Increment the accessed field for the given analysis file name.
746+
747+
Parameters
748+
----------
749+
key : Union[str, dict]
750+
The name of the analysis NWB file, or a key to the table.
751+
table : str, optional
752+
The table that created the file.
753+
"""
754+
if isinstance(key, str):
755+
key = {"analysis_file_name": key}
756+
757+
if not (query := self & key):
758+
self.log(**key, table=table)
759+
entries = query.fetch(as_dict=True)
760+
761+
inserts = []
762+
for entry in entries:
763+
entry["accessed"] += 1
764+
if table and not entry.get("table"):
765+
entry["table"] = table
766+
inserts.append(entry)
767+
768+
self.insert(inserts, replace=True)

src/spyglass/decoding/v1/waveform_features.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,8 @@ def make(self, key):
166166
nwb_file_name,
167167
key["analysis_file_name"],
168168
)
169+
AnalysisNwbfile().log(key, table=self.full_table_name)
170+
169171
self.insert1(key)
170172

171173
@staticmethod
@@ -392,9 +394,4 @@ def _write_waveform_features_to_nwb(
392394
units_object_id = nwbf.units.object_id
393395
io.write(nwbf)
394396

395-
AnalysisNwbfile().log(
396-
analysis_nwb_file,
397-
table="`decoding_waveform_features`.`__unit_waveform_features`",
398-
)
399-
400397
return analysis_nwb_file, units_object_id

src/spyglass/spikesorting/v0/spikesorting_curation.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -268,9 +268,6 @@ def save_sorting_nwb(
268268
else:
269269
units_object_id = object_ids[0]
270270

271-
AnalysisNwbfile().log(
272-
analysis_file_name, table="`spikesorting_curation`.`curation`"
273-
)
274271
return analysis_file_name, units_object_id
275272

276273

@@ -1003,6 +1000,8 @@ def make(self, key):
10031000
unit_ids=accepted_units,
10041001
labels=labels,
10051002
)
1003+
1004+
AnalysisNwbfile().log(key, table=self.full_table_name)
10061005
self.insert1(key)
10071006

10081007
# now add the units

src/spyglass/spikesorting/v1/curation.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def insert_curation(
128128
key,
129129
skip_duplicates=True,
130130
)
131+
AnalysisNwbfile().log(analysis_file_name, table=cls.full_table_name)
131132

132133
return key
133134

@@ -425,9 +426,6 @@ def _write_sorting_to_nwb_with_curation(
425426

426427
units_object_id = nwbf.units.object_id
427428
io.write(nwbf)
428-
AnalysisNwbfile().log(
429-
analysis_nwb_file, table="`spikesorting_v1_sorting`.`__spike_sorting`"
430-
)
431429
return analysis_nwb_file, units_object_id
432430

433431

src/spyglass/spikesorting/v1/metric_curation.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,7 @@ def make(self, key):
276276
nwb_file_name,
277277
key["analysis_file_name"],
278278
)
279+
AnalysisNwbfile().log(key, table=self.full_table_name)
279280
self.insert1(key)
280281

281282
@classmethod
@@ -586,8 +587,4 @@ def _write_metric_curation_to_nwb(
586587

587588
units_object_id = nwbf.units.object_id
588589
io.write(nwbf)
589-
AnalysisNwbfile().log(
590-
analysis_nwb_file,
591-
table="`spikesorting_v1_metric_curation`.`__metric_curation`",
592-
)
593590
return analysis_nwb_file, units_object_id

src/spyglass/spikesorting/v1/recording.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,6 +284,9 @@ def make(self, key):
284284
(SpikeSortingRecordingSelection & key).fetch1("nwb_file_name"),
285285
key["analysis_file_name"],
286286
)
287+
AnalysisNwbfile().log(
288+
recording_nwb_file_name, table=self.full_table_name
289+
)
287290
self.insert1(key)
288291

289292
@classmethod
@@ -651,10 +654,6 @@ def _write_recording_to_nwb(
651654
"ProcessedElectricalSeries"
652655
].object_id
653656
io.write(nwbfile)
654-
AnalysisNwbfile().log(
655-
analysis_nwb_file,
656-
table="`spikesorting_v1_sorting`.`__spike_sorting_recording`",
657-
)
658657
return analysis_nwb_file, recording_object_id
659658

660659

src/spyglass/spikesorting/v1/sorting.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,7 @@ def make(self, key: dict):
300300
(SpikeSortingSelection & key).fetch1("nwb_file_name"),
301301
key["analysis_file_name"],
302302
)
303+
AnalysisNwbfile().log(key, table=self.full_table_name)
303304
self.insert1(key, skip_duplicates=True)
304305

305306
@classmethod
@@ -405,7 +406,4 @@ def _write_sorting_to_nwb(
405406
)
406407
units_object_id = nwbf.units.object_id
407408
io.write(nwbf)
408-
AnalysisNwbfile().log(
409-
analysis_nwb_file, table="`spikesorting_v1_curation`.`curation_v1`"
410-
)
411409
return analysis_nwb_file, units_object_id

src/spyglass/utils/dj_helper_fn.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,9 @@
66

77
import datajoint as dj
88
import numpy as np
9+
from datajoint.user_tables import UserTable
910

11+
from spyglass.utils.dj_chains import PERIPHERAL_TABLES
1012
from spyglass.utils.logging import logger
1113
from spyglass.utils.nwb_helper_fn import get_nwb_file
1214

@@ -110,6 +112,26 @@ def dj_replace(original_table, new_values, key_column, replace_column):
110112
return original_table
111113

112114

115+
def get_fetching_table_from_stack(stack):
116+
"""Get all classes from a stack of tables."""
117+
classes = set()
118+
for frame_info in stack:
119+
locals_dict = frame_info.frame.f_locals
120+
for obj in locals_dict.values():
121+
if not isinstance(obj, UserTable):
122+
continue # skip non-tables
123+
if (name := obj.full_table_name) in PERIPHERAL_TABLES:
124+
continue # skip common_nwbfile tables
125+
classes.add(name)
126+
if len(classes) > 1:
127+
logger.warn(
128+
f"Multiple classes found in stack: {classes}. "
129+
"Please submit a bug report with the snippet used."
130+
)
131+
classes = None # predict only one but not sure, so return None
132+
return next(iter(classes)) if classes else None
133+
134+
113135
def get_nwb_table(query_expression, tbl, attr_name, *attrs, **kwargs):
114136
"""Get the NWB file name and path from the given DataJoint query.
115137
@@ -150,6 +172,11 @@ def get_nwb_table(query_expression, tbl, attr_name, *attrs, **kwargs):
150172
query_expression * tbl.proj(nwb2load_filepath=attr_name)
151173
).fetch(file_name_str)
152174

175+
if which == "analysis": # log access of analysis files to log table
176+
AnalysisNwbfile().increment_access(
177+
nwb_files, table=get_fetching_table_from_stack(inspect.stack())
178+
)
179+
153180
return nwb_files, file_path_fn
154181

155182

@@ -185,6 +212,7 @@ def fetch_nwb(query_expression, nwb_master, *attrs, **kwargs):
185212
nwb_files, file_path_fn = get_nwb_table(
186213
query_expression, tbl, attr_name, *attrs, **kwargs
187214
)
215+
188216
for file_name in nwb_files:
189217
file_path = file_path_fn(file_name)
190218
if not os.path.exists(file_path): # retrieve the file from kachery.

0 commit comments

Comments
 (0)