sl-shared-assets 6.1.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- sl_shared_assets/__init__.py +120 -0
- sl_shared_assets/command_line_interfaces/__init__.py +3 -0
- sl_shared_assets/command_line_interfaces/configure.py +318 -0
- sl_shared_assets/data_classes/__init__.py +121 -0
- sl_shared_assets/data_classes/configuration_data.py +939 -0
- sl_shared_assets/data_classes/dataset_data.py +385 -0
- sl_shared_assets/data_classes/processing_data.py +385 -0
- sl_shared_assets/data_classes/runtime_data.py +237 -0
- sl_shared_assets/data_classes/session_data.py +400 -0
- sl_shared_assets/data_classes/surgery_data.py +138 -0
- sl_shared_assets/data_transfer/__init__.py +12 -0
- sl_shared_assets/data_transfer/checksum_tools.py +125 -0
- sl_shared_assets/data_transfer/transfer_tools.py +181 -0
- sl_shared_assets/py.typed +0 -0
- sl_shared_assets-6.1.1.dist-info/METADATA +830 -0
- sl_shared_assets-6.1.1.dist-info/RECORD +19 -0
- sl_shared_assets-6.1.1.dist-info/WHEEL +4 -0
- sl_shared_assets-6.1.1.dist-info/entry_points.txt +2 -0
- sl_shared_assets-6.1.1.dist-info/licenses/LICENSE +674 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
"""This module provides the assets that maintain the Sun lab project data hierarchy across all machines used to
|
|
2
|
+
acquire, process, and store the data.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import copy
|
|
6
|
+
from enum import StrEnum
|
|
7
|
+
import shutil as sh
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from dataclasses import field, dataclass
|
|
10
|
+
|
|
11
|
+
from ataraxis_base_utilities import console, ensure_directory_exists
|
|
12
|
+
from ataraxis_data_structures import YamlConfig
|
|
13
|
+
from ataraxis_time.time_helpers import TimestampFormats, get_timestamp
|
|
14
|
+
|
|
15
|
+
from .configuration_data import AcquisitionSystems, get_system_configuration_data
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SessionTypes(StrEnum):
|
|
19
|
+
"""Defines the data acquisition session types supported by all data acquisition systems used in the Sun lab."""
|
|
20
|
+
|
|
21
|
+
LICK_TRAINING = "lick training"
|
|
22
|
+
"""A Mesoscope-VR session designed to teach animals to use the water delivery port while being head-fixed."""
|
|
23
|
+
RUN_TRAINING = "run training"
|
|
24
|
+
"""A Mesoscope-VR session designed to teach animals to run on the treadmill while being head-fixed."""
|
|
25
|
+
MESOSCOPE_EXPERIMENT = "mesoscope experiment"
|
|
26
|
+
"""A Mesoscope-VR experiment session. The session uses the Unity game engine to run virtual reality tasks and
|
|
27
|
+
collects brain activity data using 2-Photon Random Access Mesoscope (2P-RAM)."""
|
|
28
|
+
WINDOW_CHECKING = "window checking"
|
|
29
|
+
"""A Mesoscope-VR session designed to evaluate the quality of the cranial window implantation procedure and the
|
|
30
|
+
suitability of the animal for experiment sessions. The session uses the Mesoscope to assess the quality
|
|
31
|
+
of the cell activity data."""
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass()
|
|
35
|
+
class RawData:
|
|
36
|
+
"""Provides the paths to the directories and files that store the data acquired and losslessly preprocessed during
|
|
37
|
+
the session's data acquisition runtime.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
raw_data_path: Path = Path()
|
|
41
|
+
"""The path to the root directory that stores the session's raw data."""
|
|
42
|
+
camera_data_path: Path = Path()
|
|
43
|
+
"""The path to the directory that contains the video camera data acquired during the session's runtime."""
|
|
44
|
+
mesoscope_data_path: Path = Path()
|
|
45
|
+
"""The path to the directory that contains the Mesoscope data acquired during the session's runtime."""
|
|
46
|
+
behavior_data_path: Path = Path()
|
|
47
|
+
"""The path to the directory that contains the non-video behavior data acquired during the session's runtime."""
|
|
48
|
+
zaber_positions_path: Path = Path()
|
|
49
|
+
"""The path to the zaber_positions.yaml file that contains the snapshot of all Zaber motor positions
|
|
50
|
+
at the end of the session's runtime."""
|
|
51
|
+
session_descriptor_path: Path = Path()
|
|
52
|
+
"""The path to the session_descriptor.yaml file that contains session-specific information, such as the specific
|
|
53
|
+
task parameters and the notes made by the experimenter during the session's runtime."""
|
|
54
|
+
hardware_state_path: Path = Path()
|
|
55
|
+
"""The path to the hardware_state.yaml file that contains the partial snapshot of the configuration parameters used
|
|
56
|
+
by the data acquisition system's hardware modules during the session's runtime."""
|
|
57
|
+
surgery_metadata_path: Path = Path()
|
|
58
|
+
"""The path to the surgery_metadata.yaml file that contains the information about the surgical intervention(s)
|
|
59
|
+
performed on the animal prior to the session's runtime."""
|
|
60
|
+
session_data_path: Path = Path()
|
|
61
|
+
"""The path to the session_data.yaml file. This path is used by the SessionData instance to save itself to disk as
|
|
62
|
+
a .yaml file."""
|
|
63
|
+
experiment_configuration_path: Path = Path()
|
|
64
|
+
"""The path to the experiment_configuration.yaml file that contains the snapshot of the experiment's configuration
|
|
65
|
+
used during the session's runtime. This file is only created for experiment sessions."""
|
|
66
|
+
mesoscope_positions_path: Path = Path()
|
|
67
|
+
"""The path to the mesoscope_positions.yaml file that contains the snapshot of the imaging axes positions used
|
|
68
|
+
by the Mesoscope at the end of the session's runtime."""
|
|
69
|
+
window_screenshot_path: Path = Path()
|
|
70
|
+
"""The path to the .png screenshot of the ScanImagePC screen that communicates the visual snapshot of the
|
|
71
|
+
cranial window alignment and cell appearance at the beginning of the session's runtime."""
|
|
72
|
+
system_configuration_path: Path = Path()
|
|
73
|
+
"""The path to the system_configuration.yaml file that contains the exact snapshot of the data acquisition system
|
|
74
|
+
configuration parameters used to acquire the session's data."""
|
|
75
|
+
checksum_path: Path = Path()
|
|
76
|
+
"""The path to the ax_checksum.txt file that stores the xxHash-128 checksum of the data used to verify its
|
|
77
|
+
integrity during transmission."""
|
|
78
|
+
nk_path: Path = Path()
|
|
79
|
+
"""The path to the nk.bin file used by the sl-experiment library to mark sessions undergoing runtime initialization.
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def resolve_paths(self, root_directory_path: Path) -> None:
|
|
83
|
+
"""Resolves all paths managed by the class instance based on the input root directory path.
|
|
84
|
+
|
|
85
|
+
Args:
|
|
86
|
+
root_directory_path: The path to the top-level raw data directory of the session's data hierarchy.
|
|
87
|
+
"""
|
|
88
|
+
# Generates the managed paths
|
|
89
|
+
self.raw_data_path = root_directory_path
|
|
90
|
+
self.camera_data_path = self.raw_data_path.joinpath("camera_data")
|
|
91
|
+
self.mesoscope_data_path = self.raw_data_path.joinpath("mesoscope_data")
|
|
92
|
+
self.behavior_data_path = self.raw_data_path.joinpath("behavior_data")
|
|
93
|
+
self.zaber_positions_path = self.raw_data_path.joinpath("zaber_positions.yaml")
|
|
94
|
+
self.session_descriptor_path = self.raw_data_path.joinpath("session_descriptor.yaml")
|
|
95
|
+
self.hardware_state_path = self.raw_data_path.joinpath("hardware_state.yaml")
|
|
96
|
+
self.surgery_metadata_path = self.raw_data_path.joinpath("surgery_metadata.yaml")
|
|
97
|
+
self.session_data_path = self.raw_data_path.joinpath("session_data.yaml")
|
|
98
|
+
self.experiment_configuration_path = self.raw_data_path.joinpath("experiment_configuration.yaml")
|
|
99
|
+
self.mesoscope_positions_path = self.raw_data_path.joinpath("mesoscope_positions.yaml")
|
|
100
|
+
self.window_screenshot_path = self.raw_data_path.joinpath("window_screenshot.png")
|
|
101
|
+
self.checksum_path = self.raw_data_path.joinpath("ax_checksum.txt")
|
|
102
|
+
self.system_configuration_path = self.raw_data_path.joinpath("system_configuration.yaml")
|
|
103
|
+
self.nk_path = self.raw_data_path.joinpath("nk.bin")
|
|
104
|
+
|
|
105
|
+
def make_directories(self) -> None:
|
|
106
|
+
"""Ensures that all major subdirectories and the root directory exist, creating any missing directories."""
|
|
107
|
+
ensure_directory_exists(self.raw_data_path)
|
|
108
|
+
ensure_directory_exists(self.camera_data_path)
|
|
109
|
+
ensure_directory_exists(self.mesoscope_data_path)
|
|
110
|
+
ensure_directory_exists(self.behavior_data_path)
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@dataclass()
|
|
114
|
+
class ProcessedData:
|
|
115
|
+
"""Provides the paths to the directories and files that store the data generated by the processing pipelines from
|
|
116
|
+
the raw data.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
processed_data_path: Path = Path()
|
|
120
|
+
"""The path to the root directory that stores the session's processed data."""
|
|
121
|
+
camera_data_path: Path = Path()
|
|
122
|
+
"""The path to the directory that contains video tracking data generated by the Sun lab DeepLabCut-based
|
|
123
|
+
video processing pipeline(s)."""
|
|
124
|
+
mesoscope_data_path: Path = Path()
|
|
125
|
+
"""The path to the directory that contains processed brain activity (cell) data generated by sl-suite2p
|
|
126
|
+
processing pipelines (single-day and multi-day)."""
|
|
127
|
+
behavior_data_path: Path = Path()
|
|
128
|
+
"""The path to the directory that contains the non-video behavior data extracted from the .npz log archives by the
|
|
129
|
+
sl-behavior log processing pipeline."""
|
|
130
|
+
|
|
131
|
+
def resolve_paths(self, root_directory_path: Path) -> None:
|
|
132
|
+
"""Resolves all paths managed by the class instance based on the input root directory path.
|
|
133
|
+
|
|
134
|
+
Args:
|
|
135
|
+
root_directory_path: The path to the top-level processed data directory of the session's data hierarchy.
|
|
136
|
+
"""
|
|
137
|
+
# Generates the managed paths
|
|
138
|
+
self.processed_data_path = root_directory_path
|
|
139
|
+
self.camera_data_path = self.processed_data_path.joinpath("camera_data")
|
|
140
|
+
self.mesoscope_data_path = self.processed_data_path.joinpath("mesoscope_data")
|
|
141
|
+
self.behavior_data_path = self.processed_data_path.joinpath("behavior_data")
|
|
142
|
+
|
|
143
|
+
def make_directories(self) -> None:
|
|
144
|
+
"""Ensures that all major subdirectories and the root directory exist, creating any missing directories."""
|
|
145
|
+
ensure_directory_exists(self.processed_data_path)
|
|
146
|
+
ensure_directory_exists(self.camera_data_path)
|
|
147
|
+
ensure_directory_exists(self.behavior_data_path)
|
|
148
|
+
ensure_directory_exists(self.mesoscope_data_path)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass()
|
|
152
|
+
class TrackingData:
|
|
153
|
+
"""Provides the path to the directory that stores the .yaml and .lock files used by ProcessingTracker instances to
|
|
154
|
+
track the runtime status of the data processing pipelines working with the session's data.
|
|
155
|
+
"""
|
|
156
|
+
|
|
157
|
+
tracking_data_path: Path = Path()
|
|
158
|
+
"""The path to the root directory that stores the session's tracking data."""
|
|
159
|
+
|
|
160
|
+
def resolve_paths(self, root_directory_path: Path) -> None:
|
|
161
|
+
"""Resolves all paths managed by the class instance based on the input root directory path.
|
|
162
|
+
|
|
163
|
+
Args:
|
|
164
|
+
root_directory_path: The path to the top-level tracking data directory of the session's data hierarchy.
|
|
165
|
+
"""
|
|
166
|
+
# Generates the managed paths
|
|
167
|
+
self.tracking_data_path = root_directory_path
|
|
168
|
+
|
|
169
|
+
def make_directories(self) -> None:
|
|
170
|
+
"""Ensures that all major subdirectories and the root directory exist, creating any missing directories."""
|
|
171
|
+
ensure_directory_exists(self.tracking_data_path)
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@dataclass
|
|
175
|
+
class SessionData(YamlConfig):
|
|
176
|
+
"""Defines the structure and the metadata of a data acquisition session.
|
|
177
|
+
|
|
178
|
+
This class encapsulates the information necessary to access the session's data stored on disk and functions as the
|
|
179
|
+
entry point for all interactions with the session's data.
|
|
180
|
+
|
|
181
|
+
Notes:
|
|
182
|
+
Do not initialize this class directly. Instead, use the create() method when starting new data acquisition
|
|
183
|
+
sessions or the load() method when accessing data for an existing session.
|
|
184
|
+
|
|
185
|
+
When this class is used to create a new session, it generates the new session's name using the current UTC
|
|
186
|
+
timestamp, accurate to microseconds. This ensures that each session 'name' is unique and preserves the overall
|
|
187
|
+
session order.
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
project_name: str
|
|
191
|
+
"""The name of the project for which the session was acquired."""
|
|
192
|
+
animal_id: str
|
|
193
|
+
"""The unique identifier of the animal that participates in the session."""
|
|
194
|
+
session_name: str
|
|
195
|
+
"""The unique identifier (name) of the session."""
|
|
196
|
+
session_type: str | SessionTypes
|
|
197
|
+
"""The type of the session."""
|
|
198
|
+
acquisition_system: str | AcquisitionSystems = AcquisitionSystems.MESOSCOPE_VR
|
|
199
|
+
"""The name of the data acquisition system used to acquire the session's data"""
|
|
200
|
+
experiment_name: str | None = None
|
|
201
|
+
"""The name of the experiment performed during the session or Null (None), if the session is not an experiment
|
|
202
|
+
session."""
|
|
203
|
+
python_version: str = "3.11.13"
|
|
204
|
+
"""The Python version used to acquire session's data."""
|
|
205
|
+
sl_experiment_version: str = "3.0.0"
|
|
206
|
+
"""The sl-experiment library version used to acquire the session's data."""
|
|
207
|
+
raw_data: RawData = field(default_factory=lambda: RawData())
|
|
208
|
+
"""Defines the session's raw data hierarchy."""
|
|
209
|
+
processed_data: ProcessedData = field(default_factory=lambda: ProcessedData())
|
|
210
|
+
"""Defines the session's processed data hierarchy."""
|
|
211
|
+
tracking_data: TrackingData = field(default_factory=lambda: TrackingData())
|
|
212
|
+
"""Defines the session's tracking data hierarchy."""
|
|
213
|
+
|
|
214
|
+
def __post_init__(self) -> None:
|
|
215
|
+
"""Ensures that all instances used to define the session's data hierarchy are properly initialized."""
|
|
216
|
+
if not isinstance(self.raw_data, RawData):
|
|
217
|
+
self.raw_data = RawData() # pragma: no cover
|
|
218
|
+
|
|
219
|
+
if not isinstance(self.processed_data, ProcessedData):
|
|
220
|
+
self.processed_data = ProcessedData() # pragma: no cover
|
|
221
|
+
|
|
222
|
+
if not isinstance(self.tracking_data, TrackingData):
|
|
223
|
+
self.tracking_data = TrackingData() # pragma: no cover
|
|
224
|
+
|
|
225
|
+
@classmethod
|
|
226
|
+
def create(
|
|
227
|
+
cls,
|
|
228
|
+
project_name: str,
|
|
229
|
+
animal_id: str,
|
|
230
|
+
session_type: SessionTypes | str,
|
|
231
|
+
python_version: str,
|
|
232
|
+
sl_experiment_version: str,
|
|
233
|
+
experiment_name: str | None = None,
|
|
234
|
+
) -> SessionData:
|
|
235
|
+
"""Initializes a new data acquisition session and creates its data structure on the host-machine's filesystem.
|
|
236
|
+
|
|
237
|
+
Notes:
|
|
238
|
+
To access the data of an already existing session, use the load() method.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
project_name: The name of the project for which the session is acquired.
|
|
242
|
+
animal_id: The unique identifier of the animal participating in the session.
|
|
243
|
+
session_type: The type of the session.
|
|
244
|
+
python_version: The Python version used to acquire the session's data.
|
|
245
|
+
sl_experiment_version: The sl-experiment library version used to acquire the session's data.
|
|
246
|
+
experiment_name: The name of the experiment performed during the session or None, if the session is
|
|
247
|
+
not an experiment session.
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
An initialized SessionData instance that stores the structure and the metadata of the created session.
|
|
251
|
+
"""
|
|
252
|
+
if session_type not in SessionTypes:
|
|
253
|
+
message = (
|
|
254
|
+
f"Invalid session type '{session_type}' encountered when initializing a new data acquisition session. "
|
|
255
|
+
f"Use one of the supported session types from the SessionTypes enumeration."
|
|
256
|
+
)
|
|
257
|
+
console.error(message=message, error=ValueError)
|
|
258
|
+
|
|
259
|
+
# Acquires the UTC timestamp to use as the session name
|
|
260
|
+
session_name = str(get_timestamp(time_separator="-", output_format=TimestampFormats.STRING))
|
|
261
|
+
|
|
262
|
+
# Resolves the acquisition system configuration. This queries the acquisition system configuration data used
|
|
263
|
+
# by the machine (PC) that calls this method.
|
|
264
|
+
acquisition_system = get_system_configuration_data()
|
|
265
|
+
|
|
266
|
+
# Constructs the root session directory path
|
|
267
|
+
session_path = acquisition_system.filesystem.root_directory.joinpath(project_name, animal_id, session_name)
|
|
268
|
+
|
|
269
|
+
# Prevents creating new sessions for non-existent projects.
|
|
270
|
+
if not acquisition_system.filesystem.root_directory.joinpath(project_name).exists():
|
|
271
|
+
message = (
|
|
272
|
+
f"Unable to initialize a new data acquisition session {session_name} for the animal '{animal_id}' and "
|
|
273
|
+
f"project '{project_name}'. The project does not exist on the local machine (PC). Use the "
|
|
274
|
+
f"'sl-project create' CLI command to create the project on the local machine before creating new "
|
|
275
|
+
f"sessions."
|
|
276
|
+
)
|
|
277
|
+
console.error(message=message, error=FileNotFoundError)
|
|
278
|
+
|
|
279
|
+
# Generates the session's raw data directory. This method assumes that the session is created on the
|
|
280
|
+
# data acquisition machine that only acquires the data and does not create the other session's directories used
|
|
281
|
+
# during data processing.
|
|
282
|
+
raw_data = RawData()
|
|
283
|
+
raw_data.resolve_paths(root_directory_path=session_path.joinpath("raw_data"))
|
|
284
|
+
raw_data.make_directories() # Generates the local 'raw_data' directory tree
|
|
285
|
+
|
|
286
|
+
# Generates the SessionData instance.
|
|
287
|
+
instance = SessionData(
|
|
288
|
+
project_name=project_name,
|
|
289
|
+
animal_id=animal_id,
|
|
290
|
+
session_name=session_name,
|
|
291
|
+
session_type=session_type,
|
|
292
|
+
acquisition_system=acquisition_system.name,
|
|
293
|
+
raw_data=raw_data,
|
|
294
|
+
experiment_name=experiment_name,
|
|
295
|
+
python_version=python_version,
|
|
296
|
+
sl_experiment_version=sl_experiment_version,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
# Saves the configured instance data to the session's directory so that it can be reused during processing or
|
|
300
|
+
# preprocessing.
|
|
301
|
+
instance.save()
|
|
302
|
+
|
|
303
|
+
# Dumps the acquisition system's configuration data to the session's directory
|
|
304
|
+
acquisition_system.save(path=instance.raw_data.system_configuration_path)
|
|
305
|
+
|
|
306
|
+
if experiment_name is not None:
|
|
307
|
+
# Copies the experiment_configuration.yaml file to the session's directory
|
|
308
|
+
experiment_configuration_path = acquisition_system.filesystem.root_directory.joinpath(
|
|
309
|
+
project_name, "configuration", f"{experiment_name}.yaml"
|
|
310
|
+
)
|
|
311
|
+
sh.copy2(experiment_configuration_path, instance.raw_data.experiment_configuration_path)
|
|
312
|
+
|
|
313
|
+
# All newly created sessions are marked with the 'nk.bin' file. If the marker is not removed during runtime,
|
|
314
|
+
# the session becomes a valid target for deletion (purging) runtimes operating from the main acquisition
|
|
315
|
+
# machine of any data acquisition system.
|
|
316
|
+
instance.raw_data.nk_path.touch()
|
|
317
|
+
|
|
318
|
+
# Returns the initialized SessionData instance to caller
|
|
319
|
+
return instance
|
|
320
|
+
|
|
321
|
+
@classmethod
|
|
322
|
+
def load(cls, session_path: Path) -> SessionData:
|
|
323
|
+
"""Loads the target session's data from the specified session_data.yaml file.
|
|
324
|
+
|
|
325
|
+
Notes:
|
|
326
|
+
To create a new session, use the create() method.
|
|
327
|
+
|
|
328
|
+
Args:
|
|
329
|
+
session_path: The path to the directory where to search for the session_data.yaml file. Typically, this
|
|
330
|
+
is the path to the root session's directory, e.g.: root/project/animal/session.
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
An initialized SessionData instance that stores the loaded session's data.
|
|
334
|
+
|
|
335
|
+
Raises:
|
|
336
|
+
FileNotFoundError: If multiple or no 'session_data.yaml' file instances are found under the input directory.
|
|
337
|
+
"""
|
|
338
|
+
# To properly initialize the SessionData instance, the provided path should contain a single session_data.yaml
|
|
339
|
+
# file at any hierarchy level.
|
|
340
|
+
session_data_files = list(session_path.rglob("session_data.yaml"))
|
|
341
|
+
if len(session_data_files) != 1:
|
|
342
|
+
message = (
|
|
343
|
+
f"Unable to load the target session's data. Expected a single session_data.yaml file to be located "
|
|
344
|
+
f"under the directory tree specified by the input path: {session_path}. Instead, encountered "
|
|
345
|
+
f"{len(session_data_files)} candidate files. This indicates that the input path does not point to a "
|
|
346
|
+
f"valid session data hierarchy."
|
|
347
|
+
)
|
|
348
|
+
console.error(message=message, error=FileNotFoundError)
|
|
349
|
+
|
|
350
|
+
# If a single candidate is found (as expected), extracts it from the list and uses it to resolve the
|
|
351
|
+
# session data hierarchy.
|
|
352
|
+
session_data_path = session_data_files.pop()
|
|
353
|
+
|
|
354
|
+
# Loads the session's data from the.yaml file
|
|
355
|
+
instance: SessionData = cls.from_yaml(file_path=session_data_path)
|
|
356
|
+
|
|
357
|
+
# The method assumes that the 'donor' YAML file is always stored inside the raw_data directory of the session
|
|
358
|
+
# to be processed. Uses this heuristic to get the path to the root session's directory.
|
|
359
|
+
local_root = session_data_path.parents[1]
|
|
360
|
+
|
|
361
|
+
# RAW DATA
|
|
362
|
+
instance.raw_data.resolve_paths(root_directory_path=local_root.joinpath(local_root, "raw_data"))
|
|
363
|
+
|
|
364
|
+
# PROCESSED DATA
|
|
365
|
+
instance.processed_data.resolve_paths(root_directory_path=local_root.joinpath(local_root, "processed_data"))
|
|
366
|
+
instance.processed_data.make_directories() # Ensures that processed data hierarchy exists.
|
|
367
|
+
|
|
368
|
+
# TRACKING DATA
|
|
369
|
+
instance.tracking_data.resolve_paths(root_directory_path=local_root.joinpath(local_root, "tracking_data"))
|
|
370
|
+
instance.tracking_data.make_directories() # Ensures tracking data directories exist
|
|
371
|
+
|
|
372
|
+
# Returns the initialized SessionData instance to caller
|
|
373
|
+
return instance
|
|
374
|
+
|
|
375
|
+
def runtime_initialized(self) -> None:
|
|
376
|
+
"""Ensures that the 'nk.bin' marker file is removed from the session's raw_data directory.
|
|
377
|
+
|
|
378
|
+
Notes:
|
|
379
|
+
This service method is used by the sl-experiment library to acquire the session's data. Do not call this
|
|
380
|
+
method manually.
|
|
381
|
+
"""
|
|
382
|
+
self.raw_data.nk_path.unlink(missing_ok=True)
|
|
383
|
+
|
|
384
|
+
def save(self) -> None:
|
|
385
|
+
"""Caches the instance's data to the session's 'raw_data' directory as a 'session_data.yaml' file."""
|
|
386
|
+
# Generates a copy of the original class to avoid modifying the instance that will be used for further
|
|
387
|
+
# processing.
|
|
388
|
+
origin = copy.deepcopy(self)
|
|
389
|
+
|
|
390
|
+
# Resets all path fields to Null (None) before saving the instance to disk.
|
|
391
|
+
origin.raw_data = None # type: ignore[assignment]
|
|
392
|
+
origin.processed_data = None # type: ignore[assignment]
|
|
393
|
+
origin.tracking_data = None # type: ignore[assignment]
|
|
394
|
+
|
|
395
|
+
# Converts StringEnum instances to strings.
|
|
396
|
+
origin.session_type = str(origin.session_type)
|
|
397
|
+
origin.acquisition_system = str(origin.acquisition_system)
|
|
398
|
+
|
|
399
|
+
# Saves instance data as a .YAML file.
|
|
400
|
+
origin.to_yaml(file_path=self.raw_data.session_data_path)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"""This module provides the assets used to store animal surgery data extracted from the Sun lab surgery log."""
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass # pragma: no cover
|
|
4
|
+
|
|
5
|
+
from ataraxis_data_structures import YamlConfig # pragma: no cover
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@dataclass()
|
|
9
|
+
class SubjectData: # pragma: no cover
|
|
10
|
+
"""Stores information about the subject of the surgical intervention."""
|
|
11
|
+
|
|
12
|
+
id: int
|
|
13
|
+
"""The subject's unique identifier."""
|
|
14
|
+
ear_punch: str
|
|
15
|
+
"""The number and the locations of ear-tags used to distinguish the subject from its cage-mates."""
|
|
16
|
+
sex: str
|
|
17
|
+
"""The subject's gender."""
|
|
18
|
+
genotype: str
|
|
19
|
+
"""The subject's genotype."""
|
|
20
|
+
date_of_birth_us: int
|
|
21
|
+
"""The subject's date of birth, stored as the number of microseconds elapsed since the UTC epoch onset."""
|
|
22
|
+
weight_g: float
|
|
23
|
+
"""The subject's pre-surgery weight, in grams."""
|
|
24
|
+
cage: int
|
|
25
|
+
"""The unique identifier (number) of the cage used to house the subject after the surgery."""
|
|
26
|
+
location_housed: str
|
|
27
|
+
"""The location (room) used to house the subject after the surgery."""
|
|
28
|
+
status: str
|
|
29
|
+
"""The current subject's status (alive / deceased)."""
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
@dataclass()
|
|
33
|
+
class ProcedureData: # pragma: no cover
|
|
34
|
+
"""Stores general information about the surgical intervention."""
|
|
35
|
+
|
|
36
|
+
surgery_start_us: int
|
|
37
|
+
"""The surgery's start date and time as microseconds elapsed since UTC epoch onset."""
|
|
38
|
+
surgery_end_us: int
|
|
39
|
+
"""The surgery's stop date and time as microseconds elapsed since UTC epoch onset."""
|
|
40
|
+
surgeon: str
|
|
41
|
+
"""The surgeon's name or ID. If the intervention was carried out by multiple surgeons, the data
|
|
42
|
+
for all surgeons is stored as part of the same string."""
|
|
43
|
+
protocol: str
|
|
44
|
+
"""The number (ID) of the experiment protocol used during the surgery."""
|
|
45
|
+
surgery_notes: str
|
|
46
|
+
"""The surgeon's notes taken during the surgery."""
|
|
47
|
+
post_op_notes: str
|
|
48
|
+
"""The surgeon's notes taken during the post-surgery recovery period."""
|
|
49
|
+
surgery_quality: int = 0
|
|
50
|
+
"""The quality of the surgical intervention ob a scake from 0 to 3 inclusive. 0 indicates unusable (bad) result, 1
|
|
51
|
+
indicates usable result that does not meet the publication threshold, 2 indicates publication-grade
|
|
52
|
+
result, 3 indicates high-tier publication grade result."""
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class DrugData: # pragma: no cover
|
|
57
|
+
"""Stores information about all medical substances (drugs) administered to the subject before, during, and
|
|
58
|
+
immediately after the surgical intervention.
|
|
59
|
+
"""
|
|
60
|
+
|
|
61
|
+
lactated_ringers_solution_volume_ml: float
|
|
62
|
+
"""The volume of Lactated Ringer's Solution (LRS) administered during the surgery, in milliliters."""
|
|
63
|
+
lactated_ringers_solution_code: str
|
|
64
|
+
"""The manufacturer code or internal reference code for the administered Lactated Ringer's Solution (LRS)."""
|
|
65
|
+
ketoprofen_volume_ml: float
|
|
66
|
+
"""The volume of diluted ketoprofen administered during the surgery, in milliliters."""
|
|
67
|
+
ketoprofen_code: str
|
|
68
|
+
"""The manufacturer code or internal reference code for the administered ketoprofen."""
|
|
69
|
+
buprenorphine_volume_ml: float
|
|
70
|
+
"""The volume of diluted buprenorphine administered during the surgery, in milliliters."""
|
|
71
|
+
buprenorphine_code: str
|
|
72
|
+
"""The manufacturer code or internal reference code for the administered buprenorphine."""
|
|
73
|
+
dexamethasone_volume_ml: float
|
|
74
|
+
"""The volume of diluted dexamethasone administered during the surgery, in milliliters."""
|
|
75
|
+
dexamethasone_code: str
|
|
76
|
+
"""The manufacturer code or internal reference code for the administered dexamethasone."""
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class ImplantData: # pragma: no cover
|
|
81
|
+
"""Stores information about a single implantation procedure performed during the surgical intervention.
|
|
82
|
+
|
|
83
|
+
Multiple ImplantData instances can be used at the same time if the surgery involved multiple implantation
|
|
84
|
+
procedures.
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
implant: str
|
|
88
|
+
"""The descriptive name of the implant."""
|
|
89
|
+
implant_target: str
|
|
90
|
+
"""The name of the brain region or cranium section targeted by the implant."""
|
|
91
|
+
implant_code: str
|
|
92
|
+
"""The manufacturer code or internal reference code for the implant."""
|
|
93
|
+
implant_ap_coordinate_mm: float
|
|
94
|
+
"""The implant's antero-posterior stereotactic coordinate, in millimeters, relative to bregma."""
|
|
95
|
+
implant_ml_coordinate_mm: float
|
|
96
|
+
"""The implant's medial-lateral stereotactic coordinate, in millimeters, relative to bregma."""
|
|
97
|
+
implant_dv_coordinate_mm: float
|
|
98
|
+
"""The implant's dorsal-ventral stereotactic coordinate, in millimeters, relative to bregma."""
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@dataclass
|
|
102
|
+
class InjectionData: # pragma: no cover
|
|
103
|
+
"""Stores information about a single injection performed during the surgical intervention.
|
|
104
|
+
|
|
105
|
+
Multiple InjectionData instances can be used at the same time if the surgery involved multiple injections.
|
|
106
|
+
"""
|
|
107
|
+
|
|
108
|
+
injection: str
|
|
109
|
+
"""The descriptive name of the injection."""
|
|
110
|
+
injection_target: str
|
|
111
|
+
"""The name of the brain region targeted by the injection."""
|
|
112
|
+
injection_volume_nl: float
|
|
113
|
+
"""The volume of substance, in nanoliters, delivered during the injection."""
|
|
114
|
+
injection_code: str
|
|
115
|
+
"""The manufacturer code or internal reference code for the injected substance."""
|
|
116
|
+
injection_ap_coordinate_mm: float
|
|
117
|
+
"""The injection's antero-posterior stereotactic coordinate, in millimeters, relative to bregma."""
|
|
118
|
+
injection_ml_coordinate_mm: float
|
|
119
|
+
"""The injection's medial-lateral stereotactic coordinate, in millimeters, relative to bregma."""
|
|
120
|
+
injection_dv_coordinate_mm: float
|
|
121
|
+
"""The injection's dorsal-ventral stereotactic coordinate, in millimeters, relative to bregma."""
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
@dataclass
|
|
125
|
+
class SurgeryData(YamlConfig): # pragma: no cover
|
|
126
|
+
"""Stores information about a surgical intervention performed on an animal before data acquisition session(s)."""
|
|
127
|
+
|
|
128
|
+
subject: SubjectData
|
|
129
|
+
"""Stores information about the subject of the surgical intervention."""
|
|
130
|
+
procedure: ProcedureData
|
|
131
|
+
"""Stores general information about the surgical intervention."""
|
|
132
|
+
drugs: DrugData
|
|
133
|
+
"""Stores information about all medical substances (drugs) administered to the subject before, during, and
|
|
134
|
+
immediately after the surgical intervention."""
|
|
135
|
+
implants: list[ImplantData]
|
|
136
|
+
"""Stores information about all implantation procedures performed during the surgical intervention."""
|
|
137
|
+
injections: list[InjectionData]
|
|
138
|
+
"""Stores information about all injections (brain infusions) performed during the surgical intervention."""
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"""This package provides assets for safely transferring data between destinations available on the local filesystem
|
|
2
|
+
and efficiently removing it from the local filesystem.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from .checksum_tools import calculate_directory_checksum
|
|
6
|
+
from .transfer_tools import delete_directory, transfer_directory
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"calculate_directory_checksum",
|
|
10
|
+
"delete_directory",
|
|
11
|
+
"transfer_directory",
|
|
12
|
+
]
|