Skip to content

Commit 2b7262e

Browse files
committed
Merge branch 'master' into spikesorting-populator-v1
2 parents 57a03e6 + bc39e04 commit 2b7262e

20 files changed

+1037
-73
lines changed

CHANGELOG.md

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
### Release Notes
66

7-
Running draft to be removed immediately prior to release.
8-
When altering tables, import all foreign key references.
7+
Running draft to be removed immediately prior to release. When altering tables,
8+
import all foreign key references.
99

1010
```python
1111
#
@@ -20,23 +20,27 @@ When altering tables, import all foreign key references.
2020

2121
- Set default codecov threshold for test fail, disable patch check #1370, #1372
2222
- Simplify PR template #1370
23+
- Allow email send on space check success, clean up maintenance logging #1381
24+
- Update pynwb pin to >=2.5.0 for `TimeSeries.get_timestamps` #1385
2325

24-
### Behavior
25-
26-
- Add methods for calling moseq visualization functions #1374
27-
28-
### Decoding
29-
30-
- Ensure results directory is created if it doesn't exist #1362
31-
32-
### Position
26+
### Infrastructure
3327

34-
- Ensure video files are properly added to `DLCProject` #1367
35-
- DLC parameter handling improvements and default value corrections #1379
28+
- Auto-load within-Spyglass tables for graph operations #1368
3629

37-
### Spikesorting
30+
### Pipelines
3831

39-
- Implement short-transaction `SpikeSortingRecording.make` for v0 #1338
32+
- Behavior
33+
- Add methods for calling moseq visualization functions #1374
34+
- Common
35+
- Add tables for storing optogenetic experiment information #1312
36+
- Remove wildcard matching in `Nwbfile().get_abs_path` #1382
37+
- Decoding
38+
- Ensure results directory is created if it doesn't exist #1362
39+
- Position
40+
- Ensure video files are properly added to `DLCProject` # 1367
41+
- DLC parameter handling improvements and default value corrections #1379
42+
- Spikesorting
43+
- Implement short-transaction `SpikeSortingRecording.make` for v0 #1338
4044

4145
## [0.5.5] (Aug 6, 2025)
4246

@@ -533,3 +537,4 @@ When altering tables, import all foreign key references.
533537
[0.5.3]: https://github.com/LorenFrankLab/spyglass/releases/tag/0.5.3
534538
[0.5.4]: https://github.com/LorenFrankLab/spyglass/releases/tag/0.5.4
535539
[0.5.5]: https://github.com/LorenFrankLab/spyglass/releases/tag/0.5.5
540+
[0.5.6]: https://github.com/LorenFrankLab/spyglass/releases/tag/0.5.6

maintenance_scripts/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,9 @@ regularly as cron jobs.
6868
- `SPACE_LOG`: the path to the log file.
6969
- `SPACE_EMAIL_SRC`/`SPACE_EMAIL_PASS`: email sender settings for disk
7070
space notifications.
71-
- `SPACE_EMAIL_RECIPENTS`: a space-separated list of email addresses to
71+
- `SPACE_EMAIL_ON_PASS`: if `true`, send an email summary even if all
72+
drives are above the limit. If `false`, only send emails on failure.
73+
- `SPACE_EMAIL_RECIPIENTS`: a space-separated list of email addresses to
7274
receive disk space notifications.
7375
- Items for posting to slack:
7476
- `SLACK_TOKEN`: the token for the slack app.

maintenance_scripts/check_disk_space.sh

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ if [[ -z "${SPACE_CHECK_DRIVES}" \
1616
|| -z "${SPACE_EMAIL_RECIPIENTS}" \
1717
|| -z "${SPACE_DRIVE_LIMITS}" \
1818
|| -z "${SPACE_LOG}" \
19-
|| -z "${SPACE_ROOT_NAME}" ]] ; then
19+
|| -z "${SPACE_ROOT_NAME}" \
20+
|| -z "${SPACE_EMAIL_ON_PASS}" ]] ; then
2021
echo "Error: Missing one or more variables required for check_disk_space.sh"
2122
exit 1
2223
fi
@@ -36,7 +37,7 @@ echo "SPACE CHECK: $(date)" >> "$SPACE_LOG"
3637
EMAIL_TEMPLATE=$(cat <<-EOF
3738
From: "Spyglass" <$SPACE_EMAIL_SRC>
3839
To: %s
39-
Subject: Drive almost full: %s
40+
Subject: %s
4041
4142
%s
4243
EOF
@@ -48,7 +49,7 @@ send_email_message() {
4849
local SUBJECT="$2"
4950
local BODY="$3"
5051
EMAIL=$(printf "$EMAIL_TEMPLATE" "$RECIPIENT" "$SUBJECT" "$BODY")
51-
curl -s --url "smtps://smtp.gmail.com:465" \
52+
curl -sS --url "smtps://smtp.gmail.com:465" \
5253
--ssl-reqd \
5354
--user "$SPACE_EMAIL_SRC:$SPACE_EMAIL_PASS" \
5455
--mail-from "$SPACE_EMAIL_SRC" \
@@ -76,6 +77,9 @@ for DRIVE in $SPACE_CHECK_DRIVES; do
7677
[[ $LEN -gt $MAX_DRIVE_LEN ]] && MAX_DRIVE_LEN=$LEN
7778
done
7879

80+
OUTPUT=""
81+
FOUND_ISSUE=0
82+
7983
# Check each drive
8084
for i in "${!DRIVE_LIST[@]}"; do
8185
DRIVE="${DRIVE_LIST[$i]}"
@@ -102,14 +106,19 @@ for i in "${!DRIVE_LIST[@]}"; do
102106
else
103107
NAME="${DRIVE:1}" # Assumes first char is `/`
104108
fi
105-
printf "%-*s: %s/%s\n" "$MAX_DRIVE_LEN" "$NAME" \
106-
"$FREE_HUMAN" "$TOTAL_HUMAN" >> "$SPACE_LOG"
109+
line=$(\
110+
printf "%-*s: %s/%s\n" \
111+
"$MAX_DRIVE_LEN" "$NAME" "$FREE_HUMAN" "$TOTAL_HUMAN"\
112+
)
113+
OUTPUT+="$line\n"
107114

108115
# Do nothing if under capacity
109116
if [[ "$FREE_BYTES" -gt "$SPACE_LIMIT_BYTES" ]]; then
110117
continue
111118
fi
112119

120+
FOUND_ISSUE=1
121+
113122
# Send email alert
114123
BODY="Low space warning: ${NAME} has ${FREE_HUMAN}/${TOTAL_HUMAN} free"
115124
SUBJ="${NAME}"
@@ -119,7 +128,15 @@ for i in "${!DRIVE_LIST[@]}"; do
119128
send_slack_message "$BODY"
120129

121130
for RECIPIENT in $SPACE_EMAIL_RECIPIENTS; do
122-
send_email_message "$RECIPIENT" "$SUBJ" "$BODY"
131+
send_email_message "$RECIPIENT" "ALMOST FULL: $SUBJ" "$BODY"
123132
done
124133

125134
done
135+
136+
echo -e "$OUTPUT" >> "$SPACE_LOG"
137+
138+
if [[ "$SPACE_EMAIL_ON_PASS" == "true" ]] && [[ "$FOUND_ISSUE" == "0" ]]; then
139+
for RECIPIENT in $SPACE_EMAIL_RECIPIENTS; do
140+
send_email_message "$RECIPIENT" "Disk Space OK" "$OUTPUT"
141+
done
142+
fi

maintenance_scripts/example.env

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ export SPACE_DRIVE_LIMITS="10T 10G 3G" # space separated, same order as above
1919
export SPACE_LOG=$SPYGLASS_LOG
2020
export SPACE_EMAIL_SRC=$SPYGLASS_EMAIL_SRC
2121
export SPACE_EMAIL_PASS=$SPYGLASS_EMAIL_PASS
22+
export SPACE_EMAIL_ON_PASS=true # email only on issues
2223
export SPACE_EMAIL_RECIPIENTS="[email protected] [email protected]" # space separated
2324
export SLACK_TOKEN="xoxb-not-a-real-token-this-will-not-work" # optional, slack token
2425
export SLACK_CHANNEL="B056AQM5VFZ" # optional, slack channel

maintenance_scripts/run_jobs.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ on_fail() { # $1: error message. Echo message and send as email
4545
local content
4646
content=$(printf "$EMAIL_TEMPLATE" "$error_msg")
4747

48-
curl -o /dev/null --ssl-reqd \
48+
curl -sS -o /dev/null --ssl-reqd \
4949
--url "smtps://smtp.gmail.com:465" \
5050
--user "${SPYGLASS_EMAIL_SRC}:${SPYGLASS_EMAIL_PASS}" \
5151
--mail-from "$SPYGLASS_EMAIL_SRC" \
@@ -82,7 +82,7 @@ CONN_TEST="import datajoint as dj; dj.logger.setLevel('ERROR'); dj.conn()"
8282
conda_run python -c "$CONN_TEST" > /dev/null || \
8383
{ on_fail "Could not connect to the database"; exit 1; }
8484

85-
# Chmod new files in past 2 days
85+
# Chmod new files in past 2 days, requires sudo
8686
if $SPYGLASS_CHMOD_FILES; then
8787
find $SPYGLASS_BASE_PATH -type f -mtime -2 -exec chmod 644 {} \; || \
8888
{ on_fail "Could not chmod new files in $SPYGLASS_BASE_PATH"; exit 1; }

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ dependencies = [
5555
"probeinterface<0.3.0", # Bc some probes fail space checks
5656
"pubnub<6.4.0", # TODO: remove this when sortingview is updated
5757
"pydotplus",
58-
"pynwb>=2.2.0,<3",
58+
"pynwb>=2.5.0,<3",
5959
"ripple_detection",
6060
"seaborn",
6161
"sortingview>=0.11",

src/spyglass/common/__init__.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,13 @@
4242
)
4343
from spyglass.common.common_lab import Institution, Lab, LabMember, LabTeam
4444
from spyglass.common.common_nwbfile import AnalysisNwbfile, Nwbfile
45+
from spyglass.common.common_optogenetics import (
46+
OpticalFiberDevice,
47+
OpticalFiberImplant,
48+
OptogeneticProtocol,
49+
Virus,
50+
VirusInjection,
51+
)
4552
from spyglass.common.common_position import (
4653
IntervalLinearizationSelection,
4754
IntervalLinearizedPosition,
@@ -57,6 +64,7 @@
5764
from spyglass.common.common_session import Session
5865
from spyglass.common.common_subject import Subject
5966
from spyglass.common.common_task import Task, TaskEpoch
67+
from spyglass.common.common_user import UserEnvironment
6068
from spyglass.common.populate_all_common import populate_all_common
6169
from spyglass.common.prepopulate import populate_from_yaml, prepopulate_default
6270
from spyglass.settings import prepopulate
@@ -69,12 +77,16 @@
6977
get_raw_eseries,
7078
get_valid_intervals,
7179
)
72-
from spyglass.common.common_user import UserEnvironment
7380

7481
__all__ = [
7582
"LabTeam",
7683
"LinearizationParameters",
7784
"Nwbfile",
85+
"OpticalFiberDevice",
86+
"OpticalFiberImplant",
87+
"OptogeneticProtocol",
88+
"Virus",
89+
"VirusInjection",
7890
"PositionInfoParameters",
7991
"PositionIntervalMap",
8092
"PositionSource",

src/spyglass/common/common_behav.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ def insert_from_nwbfile(cls, nwb_file_name, skip_duplicates=False) -> None:
7878
"""
7979
nwbf = get_nwb_file(nwb_file_name)
8080
all_pos = get_all_spatial_series(nwbf, verbose=True)
81-
sess_key = Nwbfile.get_file_key(nwb_file_name)
81+
sess_key = {"nwb_file_name": nwb_file_name}
8282
src_key = dict(**sess_key, source="imported", import_file_name="")
8383

8484
if all_pos is None:

src/spyglass/common/common_nwbfile.py

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -84,24 +84,6 @@ def fetch_nwb(self):
8484
for file in self.fetch("nwb_file_name")
8585
]
8686

87-
@classmethod
88-
def _get_file_name(cls, nwb_file_name: str) -> str:
89-
"""Get valid nwb file name given substring."""
90-
query = cls & f'nwb_file_name LIKE "%{nwb_file_name}%"'
91-
92-
if len(query) == 1:
93-
return query.fetch1("nwb_file_name")
94-
95-
raise ValueError(
96-
f"Found {len(query)} matches for {nwb_file_name} in Nwbfile table:"
97-
+ f" \n{query}"
98-
)
99-
100-
@classmethod
101-
def get_file_key(cls, nwb_file_name: str) -> dict:
102-
"""Return primary key using nwb_file_name substring."""
103-
return {"nwb_file_name": cls._get_file_name(nwb_file_name)}
104-
10587
@classmethod
10688
def get_abs_path(
10789
cls, nwb_file_name: str, new_file: bool = False, **kwargs
@@ -124,10 +106,17 @@ def get_abs_path(
124106
nwb_file_abspath : str
125107
The absolute path for the given file name.
126108
"""
109+
file_path = raw_dir + "/" + nwb_file_name
127110
if new_file:
128-
return raw_dir + "/" + nwb_file_name
111+
return file_path
112+
113+
query = cls & {"nwb_file_name": nwb_file_name}
114+
if len(query) != 1:
115+
raise ValueError(
116+
f"Could not find 1 entry for {nwb_file_name}:\n{query}"
117+
)
129118

130-
return raw_dir + "/" + cls._get_file_name(nwb_file_name)
119+
return file_path
131120

132121
@staticmethod
133122
def add_to_lock(nwb_file_name: str) -> None:

0 commit comments

Comments
 (0)