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.
@@ -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
+ ]