Data pipeline¶
Every trial from every box ends up in a DataJoint database (MySQL on AWS), organized by animal, box, protocol and trial, so anyone in the lab can query it from Python.
flowchart LR
B[Bpod boxes] -->|SessionData .mat<br/>new file every 24 h| L["Rig computer<br/>Bpod Local/Data"]
L -->|01:00 rsync<br/>transferBpodData| N[("Lab NAS<br/>RATACAD_DATA")]
N -->|02:00<br/>dj-ratacad update| D[("DataJoint / MySQL<br/>on AWS RDS")]
D --> U[Analysis in Python<br/>or R via djreadr]
1. Bpod → rig computer¶
The protocol saves Data/<Subject>/<Protocol>/Session Data/<Subject>_<Protocol>_<YYYYMMDD>_<HHMMSS>.mat
throughout the session. The NoGUI Bpod adds
Info.BpodName (which box) and starts a new file every 24 h.
Naming rule
File names are parsed by splitting on _ into exactly four parts. Subject and
protocol names must not contain underscores, or those files are skipped.
2. Rig computer → NAS¶
transferBpodData runs at 01:00 and rsyncs Bpod Local/Data/*
to RATACAD_DATA on the NAS. On Ubuntu it then deletes local files older than 30 days.
3. NAS → DataJoint¶
One computer, which has both the NAS mounted and database access, runs
updateDJ.sh every day at 02:00. The script reinstalls
dj_ratacad from GitHub and runs dj-ratacad update. It logs to ~/dj_ratacad.log.
Example crontab entry:
dj-ratacad update does the following:
bpod.BpodMetadata: for each animal inanimal.Animalthat isn't euthanized, it finds new.matfiles under$RATACAD_DATA_DIR/<animal>/*/Session Data/and records the session time, box (fromInfo.BpodName), protocol and settings.bpod.BpodTrialData: adds any new trials, including from sessions that are still running. A file is marked closed (FileClosed) after 3 days with no new trials.- Task tables: populates each task's
*TrialandDailySummarytables in parallel.
Schemas¶
| Schema | Tables | Contents |
|---|---|---|
animal |
Animal, Weight, Details, Euthanized |
animals (name, strain, DOB, sex), weights (with % of ad-lib baseline) |
bpod |
BoxDesign, Bpod, Protocol, BpodMetadata, BpodTrialData, FileClosed |
box inventory (3-port, 6-port, lever-port), one row per file and per trial |
flashes |
FlashesTrial, DailySummary |
flash-accumulation task |
flashcount |
FlashCountTrial, DailySummary |
flash-counting task |
twostep |
TwoStepTrial, DailySummary |
two-step task (6-port box) |
timingtask |
TimingtaskTrialV2, DailySummary |
lever interval-timing task |
probabilities |
ProbabilitiesTrial, DailySummary |
two-armed bandit |
uncertainflashinference |
Trials, DailySummary, StageSummary |
populated by MATLAB (dj_ratacad_matlab/) |
An entity-relationship diagram is in the repo:
images/dj_ratacad_erd.png.
Install dj_ratacad¶
conda create -n dj python=3.10
conda activate dj
pip install git+https://github.com/RatAcad/dj_ratacad
Set up your database credentials once (you need a database account):
import datajoint as dj
dj.config["database.host"] = "<DB_HOST>"
dj.config["database.user"] = "<your_username>"
dj.config["database.password"] = "<your_password>"
dj.config.save_global() # stores ~/.datajoint_config.json
The ingest computer also needs the NAS path:
Adding animals¶
An animal must be in animal.Animal before its data is ingested.
from dj_ratacad import animal
animal.Animal.insert1({
"name": "MyRat", # must match the Bpod subject name, no underscores
"species": "Rat",
"strain": "Long-Evans",
"id": 12345, # e.g. ear tag
"dob": "2026-04-01",
"pob": "BU",
"sex": "M",
})
Weights can be recorded from the command line:
dj-ratacad weight MyRat -d 2026-10-02 -w 412 -b # -b = ad-lib baseline weight
dj-ratacad weight MyRat -d 2026-10-09 -w 371 # % of baseline is computed
dj-ratacad weight MyRat -v # list weights
Adding boxes and tasks¶
- New or changed box: add a new row to
bpod.Bpod(box name, design, date, serial). Never edit old rows, because sessions are matched to the box version that was current at the time. If the box design is new, add it tobpod.BoxDesigntoo. - New task: add a row to
bpod.Protocol, create the MySQL schema (<prefix>_<task>), writedj_ratacad/<task>.pywith<Task>TrialandDailySummary, and register apopulate_<task>()process incli.py. Do this on a branch and open a pull request.
Querying data¶
from dj_ratacad import flashes
trials = (flashes.FlashesTrial() & 'name="MyRat"').fetch(format="frame")
stage5 = (flashes.FlashesTrial() & "stage=5").fetch(as_dict=True)
In R, use djreadr.
Building your own academy?
You don't need AWS specifically. Any MySQL 8 server works, e.g. a lab server,
a DataJoint-hosted database or Amazon RDS. Change the
schema prefix (scott_…) in dj_ratacad to your own.