sl-shared-assets 1.0.0rc13__py3-none-any.whl → 1.0.0rc14__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.

Potentially problematic release.


This version of sl-shared-assets might be problematic. Click here for more details.

@@ -0,0 +1,799 @@
1
+ from pathlib import Path
2
+ from dataclasses import field, dataclass
3
+
4
+ from _typeshed import Incomplete
5
+ from ataraxis_data_structures import YamlConfig
6
+
7
+ def replace_root_path(path: Path) -> None:
8
+ """Replaces the path to the local root directory used to store all Sun lab projects with the provided path.
9
+
10
+ The first time ProjectConfiguration class is instantiated to create a new project on a new machine,
11
+ it asks the user to provide the path to the local directory where to save all Sun lab projects. This path is then
12
+ stored inside the default user data directory as a .yaml file to be reused for all future projects. To support
13
+ replacing this path without searching for the user data directory, which is usually hidden, this function finds and
14
+ updates the contents of the file that stores the local root path.
15
+
16
+ Args:
17
+ path: The path to the new local root directory.
18
+ """
19
+ @dataclass()
20
+ class ProjectConfiguration(YamlConfig):
21
+ """Stores the project-specific configuration parameters that do not change between different animals and runtime
22
+ sessions.
23
+
24
+ An instance of this class is generated and saved as a .yaml file in the \'configuration\' directory of each project
25
+ when it is created. After that, the stored data is reused for every runtime (training or experiment session) carried
26
+ out for each animal of the project. Additionally, a copy of the most actual configuration file is saved inside each
27
+ runtime session\'s \'raw_data\' folder, providing seamless integration between the managed data and various Sun lab
28
+ (sl-) libraries.
29
+
30
+ Notes:
31
+ Together with SessionData, this class forms the entry point for all interactions with the data acquired in the
32
+ Sun lab. The fields of this class are used to flexibly configure the runtime behavior of major data acquisition
33
+ (sl-experiment) and processing (sl-forgery) libraries, adapting them for any project in the lab.
34
+
35
+ Most lab projects only need to adjust the "surgery_sheet_id" and "water_log_sheet_id" fields of the class. Most
36
+ fields in this class are used by the sl-experiment library to generate the SessionData class instance for each
37
+ session and during experiment data acquisition and preprocessing. Data processing pipelines use specialized
38
+ configuration files stored in other modules of this library.
39
+
40
+ Although all path fields use str | Path datatype, they are always stored as Path objects. These fields are
41
+ converted to strings only when the data is dumped as a .yaml file.
42
+ """
43
+
44
+ project_name: str = ...
45
+ surgery_sheet_id: str = ...
46
+ water_log_sheet_id: str = ...
47
+ google_credentials_path: str | Path = ...
48
+ server_credentials_path: str | Path = ...
49
+ local_root_directory: str | Path = ...
50
+ local_server_directory: str | Path = ...
51
+ local_nas_directory: str | Path = ...
52
+ local_mesoscope_directory: str | Path = ...
53
+ local_server_working_directory: str | Path = ...
54
+ remote_storage_directory: str | Path = ...
55
+ remote_working_directory: str | Path = ...
56
+ face_camera_index: int = ...
57
+ left_camera_index: int = ...
58
+ right_camera_index: int = ...
59
+ harvesters_cti_path: str | Path = ...
60
+ actor_port: str = ...
61
+ sensor_port: str = ...
62
+ encoder_port: str = ...
63
+ headbar_port: str = ...
64
+ lickport_port: str = ...
65
+ unity_ip: str = ...
66
+ unity_port: int = ...
67
+ valve_calibration_data: dict[int | float, int | float] | tuple[tuple[int | float, int | float], ...] = ...
68
+ @classmethod
69
+ def load(cls, project_name: str, configuration_path: None | Path = None) -> ProjectConfiguration:
70
+ """Loads the project configuration parameters from a project_configuration.yaml file.
71
+
72
+ This method is called during each interaction with any runtime session's data, including the creation of a new
73
+ session. When this method is called for a non-existent (new) project name, it generates the default
74
+ configuration file and prompts the user to update the configuration before proceeding with the runtime. All
75
+ future interactions with the sessions from this project reuse the existing configuration file.
76
+
77
+ Notes:
78
+ As part of its runtime, the method may prompt the user to provide the path to the local root directory.
79
+ This directory stores all project subdirectories and acts as the top level of the Sun lab data hierarchy.
80
+ The path to the directory is then saved inside user's default data directory, so that it can be reused for
81
+ all future projects. Use sl-replace-root CLI to replace the saved root directory path.
82
+
83
+ Since this class is used for all Sun lab data structure interactions, this method supports multiple ways of
84
+ loading class data. If this method is called as part of the sl-experiment new session creation pipeline, use
85
+ 'project_name' argument. If this method is called as part of the sl-forgery data processing pipeline(s), use
86
+ 'configuration_path' argument.
87
+
88
+ Args:
89
+ project_name: The name of the project whose configuration file needs to be discovered and loaded or, if the
90
+ project does not exist, created.
91
+ configuration_path: Optional. The path to the project_configuration.yaml file from which to load the data.
92
+ This way of resolving the configuration data source always takes precedence over the project_name when
93
+ both are provided.
94
+
95
+ Returns:
96
+ The initialized ProjectConfiguration instance that stores the configuration data for the target project.
97
+ """
98
+ def save(self, path: Path) -> None:
99
+ """Saves class instance data to disk as a project_configuration.yaml file.
100
+
101
+ This method is automatically called when a new project is created. After this method's runtime, all future
102
+ calls to the load() method will reuse the configuration data saved to the .yaml file.
103
+
104
+ Notes:
105
+ When this method is used to generate the configuration .yaml file for a new project, it also generates the
106
+ example 'default_experiment.yaml'. This file is designed to showcase how to write ExperimentConfiguration
107
+ data files that are used to control Mesoscope-VR system states during experiment session runtimes.
108
+
109
+ Args:
110
+ path: The path to the .yaml file to save the data to.
111
+ """
112
+ def _verify_data(self) -> None:
113
+ """Verifies the user-modified data loaded from the project_configuration.yaml file.
114
+
115
+ Since this class is explicitly designed to be modified by the user, this verification step is carried out to
116
+ ensure that the loaded data matches expectations. This reduces the potential for user errors to impact the
117
+ runtime behavior of the libraries using this class. This internal method is automatically called by the load()
118
+ method.
119
+
120
+ Notes:
121
+ The method does not verify all fields loaded from the configuration file and instead focuses on fields that
122
+ do not have valid default values. Since these fields are expected to be frequently modified by users, they
123
+ are the ones that require additional validation.
124
+
125
+ Raises:
126
+ ValueError: If the loaded data does not match expected formats or values.
127
+ """
128
+
129
+ @dataclass()
130
+ class RawData:
131
+ """Stores the paths to the directories and files that make up the 'raw_data' session-specific directory.
132
+
133
+ The raw_data directory stores the data acquired during the session runtime before and after preprocessing. Since
134
+ preprocessing does not alter the data, any data in that folder is considered 'raw'. The raw_data folder is initially
135
+ created on the VRPC and, after preprocessing, is copied to the BioHPC server and the Synology NAS for long-term
136
+ storage and further processing.
137
+ """
138
+
139
+ raw_data_path: Path = ...
140
+ camera_data_path: Path = ...
141
+ mesoscope_data_path: Path = ...
142
+ behavior_data_path: Path = ...
143
+ zaber_positions_path: Path = ...
144
+ session_descriptor_path: Path = ...
145
+ hardware_configuration_path: Path = ...
146
+ surgery_metadata_path: Path = ...
147
+ project_configuration_path: Path = ...
148
+ session_data_path: Path = ...
149
+ experiment_configuration_path: Path = ...
150
+ mesoscope_positions_path: Path = ...
151
+ window_screenshot_path: Path = ...
152
+ telomere_path: Path = ...
153
+ checksum_path: Path = ...
154
+ def resolve_paths(self, root_directory_path: Path) -> None:
155
+ """Resolves all paths managed by the class instance based on the input root directory path.
156
+
157
+ This method is called each time the class is instantiated to regenerate the managed path hierarchy on any
158
+ machine that instantiates the class.
159
+
160
+ Args:
161
+ root_directory_path: The path to the top-level directory of the local hierarchy. Depending on the managed
162
+ hierarchy, this has to point to a directory under the main /session, /animal, or /project directory of
163
+ the managed session.
164
+ """
165
+ def make_directories(self) -> None:
166
+ """Ensures that all major subdirectories and the root directory exist."""
167
+
168
+ @dataclass()
169
+ class DeepLabCutData:
170
+ """Stores the paths to the directories and files that make up the 'deeplabcut' project-specific directory.
171
+
172
+ DeepLabCut (DLC) is used to track animal body parts and poses in video data acquired during experiment and training
173
+ sessions. Since DLC is designed to work with projects, rather than single animals or sessions, each Sun lab
174
+ project data hierarchy contains a dedicated 'deeplabcut' directory under the root project directory. The contents of
175
+ that directory are largely managed by the DLC itself. Therefore, each session of a given project refers to and
176
+ uses the same 'deeplabcut' directory.
177
+ """
178
+
179
+ deeplabcut_path: Path = ...
180
+ def resolve_paths(self, root_directory_path: Path) -> None:
181
+ """Resolves all paths managed by the class instance based on the input root directory path.
182
+
183
+ This method is called each time the class is instantiated to regenerate the managed path hierarchy on any
184
+ machine that instantiates the class.
185
+
186
+ Args:
187
+ root_directory_path: The path to the top-level directory of the local hierarchy. Depending on the managed
188
+ hierarchy, this has to point to a directory under the main /session, /animal, or /project directory of
189
+ the managed session.
190
+ """
191
+ def make_directories(self) -> None:
192
+ """Ensures that all major subdirectories and the root directory exist."""
193
+
194
+ @dataclass()
195
+ class ConfigurationData:
196
+ """Stores the paths to the directories and files that make up the 'configuration' project-specific directory.
197
+
198
+ The configuration directory contains various configuration files and settings used by data acquisition,
199
+ preprocessing, and processing pipelines in the lab. Generally, all configuration settings are defined once for each
200
+ project and are reused for every session within the project. Therefore, this directory is created under each main
201
+ project directory.
202
+
203
+ Notes:
204
+ Some attribute names inside this section match the names in the RawData section. This is intentional, as some
205
+ configuration files are copied into the raw_data session directories to allow reinstating the session data
206
+ hierarchy across machines.
207
+ """
208
+
209
+ configuration_path: Path = ...
210
+ experiment_configuration_path: Path = ...
211
+ project_configuration_path: Path = ...
212
+ suite2p_configuration_path: Path = ...
213
+ multiday_configuration_path: Path = ...
214
+ def resolve_paths(self, root_directory_path: Path, experiment_name: str | None = None) -> None:
215
+ """Resolves all paths managed by the class instance based on the input root directory path.
216
+
217
+ This method is called each time the class is instantiated to regenerate the managed path hierarchy on any
218
+ machine that instantiates the class.
219
+
220
+ Args:
221
+ root_directory_path: The path to the top-level directory of the local hierarchy. Depending on the managed
222
+ hierarchy, this has to point to a directory under the main /session, /animal, or /project directory of
223
+ the managed session.
224
+ experiment_name: Optionally specifies the name of the experiment executed as part of the managed session's
225
+ runtime. This is used to correctly configure the path to the specific ExperimentConfiguration data file.
226
+ If the managed session is not an Experiment session, this parameter should be set to None.
227
+ """
228
+ def make_directories(self) -> None:
229
+ """Ensures that all major subdirectories and the root directory exist."""
230
+
231
+ @dataclass()
232
+ class ProcessedData:
233
+ """Stores the paths to the directories and files that make up the 'processed_data' session-specific directory.
234
+
235
+ The processed_data directory stores the data generated by various processing pipelines from the raw data (contents
236
+ of the raw_data directory). Processed data represents an intermediate step between raw data and the dataset used in
237
+ the data analysis, but is not itself designed to be analyzed.
238
+
239
+ Notes:
240
+ The paths from this section are typically used only on the BioHPC server. This is because most data processing
241
+ in the lab is performed using the processing server's resources. On the server, processed data is stored on
242
+ the fast (NVME) drive volume, in contrast to raw data, which is stored on the slow (SSD) drive volume.
243
+
244
+ When this class is instantiated on a machine other than BioHPC server, for example, to test processing
245
+ pipelines, it uses the same drive as the raw_data folder to create the processed_data folder. This relies on the
246
+ assumption that non-server machines in the lab only use fast NVME drives, so there is no need to separate
247
+ storage and processing volumes.
248
+ """
249
+
250
+ processed_data_path: Path = ...
251
+ camera_data_path: Path = ...
252
+ mesoscope_data_path: Path = ...
253
+ behavior_data_path: Path = ...
254
+ job_logs_path: Path = ...
255
+ processing_tracker_path: Path = ...
256
+ def resolve_paths(self, root_directory_path: Path) -> None:
257
+ """Resolves all paths managed by the class instance based on the input root directory path.
258
+
259
+ This method is called each time the class is instantiated to regenerate the managed path hierarchy on any
260
+ machine that instantiates the class.
261
+
262
+ Args:
263
+ root_directory_path: The path to the top-level directory of the local hierarchy. Depending on the managed
264
+ hierarchy, this has to point to a directory under the main /session, /animal, or /project directory of
265
+ the managed session.
266
+ """
267
+ def make_directories(self) -> None:
268
+ """Ensures that all major subdirectories and the root directory exist."""
269
+
270
+ @dataclass()
271
+ class VRPCPersistentData:
272
+ """Stores the paths to the directories and files that make up the 'persistent_data' directory on the VRPC.
273
+
274
+ Persistent data directories are only used during data acquisition. Therefore, unlike most other directories, they
275
+ are purposefully designed for specific PCs that participate in data acquisition. This section manages the
276
+ animal-specific persistent_data directory stored on the VRPC.
277
+
278
+ VRPC persistent data directory is used to preserve configuration data, such as the positions of Zaber motors and
279
+ Meososcope objective, so that they can be reused across sessions of the same animals. The data in this directory
280
+ is read at the beginning of each session and replaced at the end of each session.
281
+ """
282
+
283
+ persistent_data_path: Path = ...
284
+ zaber_positions_path: Path = ...
285
+ mesoscope_positions_path: Path = ...
286
+ def resolve_paths(self, root_directory_path: Path) -> None:
287
+ """Resolves all paths managed by the class instance based on the input root directory path.
288
+
289
+ This method is called each time the class is instantiated to regenerate the managed path hierarchy on any
290
+ machine that instantiates the class.
291
+
292
+ Args:
293
+ root_directory_path: The path to the top-level directory of the local hierarchy. Depending on the managed
294
+ hierarchy, this has to point to a directory under the main /session, /animal, or /project directory of
295
+ the managed session.
296
+ """
297
+ def make_directories(self) -> None:
298
+ """Ensures that all major subdirectories and the root directory exist."""
299
+
300
+ @dataclass()
301
+ class ScanImagePCPersistentData:
302
+ """Stores the paths to the directories and files that make up the 'persistent_data' directory on the ScanImagePC.
303
+
304
+ Persistent data directories are only used during data acquisition. Therefore, unlike most other directories, they
305
+ are purposefully designed for specific PCs that participate in data acquisition. This section manages the
306
+ animal-specific persistent_data directory stored on the ScanImagePC (Mesoscope PC).
307
+
308
+ ScanImagePC persistent data directory is used to preserve the motion estimation snapshot, generated during the first
309
+ experiment session. This is necessary to align the brain recording field of view across sessions. In turn, this
310
+ is used to carry out 'online' motion and z-drift correction, improving the accuracy of across-day (multi-day)
311
+ cell tracking.
312
+ """
313
+
314
+ persistent_data_path: Path = ...
315
+ motion_estimator_path: Path = ...
316
+ def resolve_paths(self, root_directory_path: Path) -> None:
317
+ """Resolves all paths managed by the class instance based on the input root directory path.
318
+
319
+ This method is called each time the class is instantiated to regenerate the managed path hierarchy on any
320
+ machine that instantiates the class.
321
+
322
+ Args:
323
+ root_directory_path: The path to the top-level directory of the local hierarchy. Depending on the managed
324
+ hierarchy, this has to point to a directory under the main /session, /animal, or /project directory of
325
+ the managed session.
326
+ """
327
+ def make_directories(self) -> None:
328
+ """Ensures that all major subdirectories and the root directory exist."""
329
+
330
+ @dataclass()
331
+ class MesoscopeData:
332
+ """Stores the paths to the directories and files that make up the 'meso_data' directory on the ScanImagePC.
333
+
334
+ The meso_data directory is the root directory where all mesoscope-generated data is stored on the ScanImagePC. The
335
+ path to this directory should be given relative to the VRPC root and be mounted to the VRPC filesystem via the
336
+ SMB or equivalent protocol.
337
+
338
+ During runtime, the ScanImagePC should organize all collected data under this root directory. During preprocessing,
339
+ the VRPC uses SMB to access the data in this directory and merge it into the 'raw_data' session directory. The paths
340
+ in this section, therefore, are specific to the VRPC and are not used on other PCs.
341
+ """
342
+
343
+ meso_data_path: Path = ...
344
+ mesoscope_data_path: Path = ...
345
+ session_specific_path: Path = ...
346
+ ubiquitin_path: Path = ...
347
+ def resolve_paths(self, root_mesoscope_path: Path, session_name: str) -> None:
348
+ """Resolves all paths managed by the class instance based on the input root directory path.
349
+
350
+ This method is called each time the class is instantiated to regenerate the managed path hierarchy on any
351
+ machine that instantiates the class.
352
+
353
+ Args:
354
+ root_mesoscope_path: The path to the top-level directory of the ScanImagePC data hierarchy mounted to the
355
+ VRPC via the SMB or equivalent protocol.
356
+ session_name: The name of the session for which this subclass is initialized.
357
+ """
358
+ def make_directories(self) -> None:
359
+ """Ensures that all major subdirectories and the root directory exist."""
360
+
361
+ @dataclass()
362
+ class VRPCDestinations:
363
+ """Stores the paths to the VRPC filesystem-mounted directories of the Synology NAS and BioHPC server.
364
+
365
+ The paths from this section are primarily used to transfer preprocessed data to the long-term storage destinations.
366
+ Additionally, they allow VRPC to interface with the configuration directory of the BioHPC server to start data
367
+ processing jobs and to read the data from the processed_data directory to remove redundant data from the VRPC
368
+ filesystem.
369
+
370
+ Overall, this section is intended solely for the VRPC and should not be used on other PCs.
371
+ """
372
+
373
+ nas_raw_data_path: Path = ...
374
+ server_raw_data_path: Path = ...
375
+ server_processed_data_path: Path = ...
376
+ server_configuration_path: Path = ...
377
+ telomere_path: Path = ...
378
+ suite2p_configuration_path: Path = ...
379
+ processing_tracker_path: Path = ...
380
+ multiday_configuration_path: Path = ...
381
+ def resolve_paths(
382
+ self,
383
+ nas_raw_data_path: Path,
384
+ server_raw_data_path: Path,
385
+ server_processed_data_path: Path,
386
+ server_configuration_path: Path,
387
+ ) -> None:
388
+ """Resolves all paths managed by the class instance based on the input root directory paths.
389
+
390
+ This method is called each time the class is instantiated to regenerate the managed path hierarchy on any
391
+ machine that instantiates the class.
392
+
393
+ Args:
394
+ nas_raw_data_path: The path to the session's raw_data directory on the Synology NAS, relative to the VRPC
395
+ filesystem root.
396
+ server_raw_data_path: The path to the session's raw_data directory on the BioHPC server, relative to the
397
+ VRPC filesystem root.
398
+ server_processed_data_path: The path to the session's processed_data directory on the BioHPC server,
399
+ relative to the VRPC filesystem root.
400
+ server_configuration_path: The path to the project-specific 'configuration' directory on the BioHPC server,
401
+ relative to the VRPC filesystem root.
402
+ """
403
+ def make_directories(self) -> None:
404
+ """Ensures that all major subdirectories and the root directory exist."""
405
+
406
+ @dataclass
407
+ class SessionData(YamlConfig):
408
+ """Stores and manages the data layout of a single training or experiment session acquired using the Sun lab
409
+ Mesoscope-VR system.
410
+
411
+ The primary purpose of this class is to maintain the session data structure across all supported destinations and
412
+ during all processing stages. It generates the paths used by all other classes from all Sun lab libraries that
413
+ interact with the session's data from the point of its creation and until the data is integrated into an
414
+ analysis dataset.
415
+
416
+ When necessary, the class can be used to either generate a new session or load the layout of an already existing
417
+ session. When the class is used to create a new session, it generates the new session's name using the current
418
+ UTC timestamp, accurate to microseconds. This ensures that each session name is unique and preserves the overall
419
+ session order.
420
+
421
+ Notes:
422
+ If this class is instantiated on the VRPC, it is expected that the BioHPC server, Synology NAS, and ScanImagePC
423
+ data directories are mounted on the local filesystem via the SMB or equivalent protocol. All manipulations
424
+ with these destinations are carried out with the assumption that the local OS has full access to these
425
+ directories and filesystems.
426
+
427
+ This class is specifically designed for working with the data from a single session, performed by a single
428
+ animal under the specific experiment. The class is used to manage both raw and processed data. It follows the
429
+ data through acquisition, preprocessing and processing stages of the Sun lab data workflow. Together with
430
+ ProjectConfiguration class, this class serves as an entry point for all interactions with the managed session's
431
+ data.
432
+ """
433
+
434
+ project_name: str
435
+ animal_id: str
436
+ session_name: str
437
+ session_type: str
438
+ experiment_name: str | None
439
+ raw_data: RawData = field(default_factory=Incomplete)
440
+ processed_data: ProcessedData = field(default_factory=Incomplete)
441
+ deeplabcut_data: DeepLabCutData = field(default_factory=Incomplete)
442
+ configuration_data: ConfigurationData = field(default_factory=Incomplete)
443
+ vrpc_persistent_data: VRPCPersistentData = field(default_factory=Incomplete)
444
+ scanimagepc_persistent_data: ScanImagePCPersistentData = field(default_factory=Incomplete)
445
+ mesoscope_data: MesoscopeData = field(default_factory=Incomplete)
446
+ destinations: VRPCDestinations = field(default_factory=Incomplete)
447
+ @classmethod
448
+ def create(
449
+ cls,
450
+ animal_id: str,
451
+ session_type: str,
452
+ project_configuration: ProjectConfiguration,
453
+ experiment_name: str | None = None,
454
+ session_name: str | None = None,
455
+ ) -> SessionData:
456
+ """Creates a new SessionData object and generates the new session's data structure.
457
+
458
+ This method is called by sl-experiment runtimes that create new training or experiment sessions to generate the
459
+ session data directory tree. It always assumes it is called on the VRPC and, as part of its runtime, resolves
460
+ and generates the necessary local and ScanImagePC directories to support acquiring and preprocessing session's
461
+ data.
462
+
463
+ Notes:
464
+ To load an already existing session data structure, use the load() method instead.
465
+
466
+ This method automatically dumps the data of the created SessionData instance into the session_data.yaml file
467
+ inside the root raw_data directory of the created hierarchy. It also finds and dumps other configuration
468
+ files, such as project_configuration.yaml and experiment_configuration.yaml, into the same raw_data
469
+ directory. This ensures that if the session's runtime is interrupted unexpectedly, the acquired data can
470
+ still be processed.
471
+
472
+ Args:
473
+ animal_id: The ID code of the animal for which the data is acquired.
474
+ session_type: The type of the session. Primarily, this determines how to read the session_descriptor.yaml
475
+ file. Valid options are 'Lick training', 'Run training', 'Window checking', or 'Experiment'.
476
+ experiment_name: The name of the experiment executed during managed session. This optional argument is only
477
+ used for 'Experiment' session types. It is used to find the experiment configuration .YAML file.
478
+ project_configuration: The initialized ProjectConfiguration instance that stores the session's project
479
+ configuration data. This is used to determine the root directory paths for all lab machines used during
480
+ data acquisition and processing.
481
+ session_name: An optional session_name override. Generally, this argument should not be provided for most
482
+ sessions. When provided, the method uses this name instead of generating a new timestamp-based name.
483
+ This is only used during the 'ascension' runtime to convert old data structures to the modern
484
+ lab standards.
485
+
486
+ Returns:
487
+ An initialized SessionData instance that stores the layout of the newly created session's data.
488
+ """
489
+ @classmethod
490
+ def load(cls, session_path: Path, on_server: bool) -> SessionData:
491
+ """Loads the SessionData instance from the target session's session_data.yaml file.
492
+
493
+ This method is used to load the data layout information of an already existing session. Primarily, this is used
494
+ when preprocessing or processing session data. Depending on the call location (machine), the method
495
+ automatically resolves all necessary paths and creates the necessary directories.
496
+
497
+ Notes:
498
+ To create a new session, use the create() method instead.
499
+
500
+ Args:
501
+ session_path: The path to the root directory of an existing session, e.g.: vrpc_root/project/animal/session.
502
+ on_server: Determines whether the method is used to initialize an existing session on the BioHPC server or
503
+ a non-server machine. Note, non-server runtimes use the same 'root' directory to store raw_data and
504
+ processed_data subfolders. BioHPC server runtimes use different volumes (drives) to store these
505
+ subfolders.
506
+
507
+ Returns:
508
+ An initialized SessionData instance for the session whose data is stored at the provided path.
509
+
510
+ Raises:
511
+ FileNotFoundError: If the 'session_data.yaml' file is not found under the session_path/raw_data/ subfolder.
512
+ """
513
+ def _save(self) -> None:
514
+ """Saves the instance data to the 'raw_data' directory of the managed session as a 'session_data.yaml' file.
515
+
516
+ This is used to save the data stored in the instance to disk, so that it can be reused during preprocessing or
517
+ data processing. The method is intended to only be used by the SessionData instance itself during its
518
+ create() method runtime.
519
+ """
520
+
521
+ @dataclass()
522
+ class ExperimentState:
523
+ """Encapsulates the information used to set and maintain the desired experiment and Mesoscope-VR system state.
524
+
525
+ Primarily, experiment runtime logic (task logic) is resolved by the Unity game engine. However, the Mesoscope-VR
526
+ system configuration may also need to change throughout the experiment to optimize the runtime by disabling or
527
+ reconfiguring specific hardware modules. For example, some experiment stages may require the running wheel to be
528
+ locked to prevent the animal from running, and other may require the VR screens to be turned off.
529
+ """
530
+
531
+ experiment_state_code: int
532
+ vr_state_code: int
533
+ state_duration_s: float
534
+
535
+ @dataclass()
536
+ class ExperimentConfiguration(YamlConfig):
537
+ """Stores the configuration of a single experiment runtime.
538
+
539
+ Primarily, this includes the sequence of experiment and Virtual Reality (Mesoscope-VR) states that defines the flow
540
+ of the experiment runtime. During runtime, the main runtime control function traverses the sequence of states
541
+ stored in this class instance start-to-end in the exact order specified by the user. Together with custom Unity
542
+ projects that define the task logic (how the system responds to animal interactions with the VR system) this class
543
+ allows flexibly implementing a wide range of experiments.
544
+
545
+ Each project should define one or more experiment configurations and save them as .yaml files inside the project
546
+ 'configuration' folder. The name for each configuration file is defined by the user and is used to identify and load
547
+ the experiment configuration when 'sl-run-experiment' CLI command exposed by the sl-experiment library is executed.
548
+ """
549
+
550
+ cue_map: dict[int, float] = field(default_factory=Incomplete)
551
+ experiment_states: dict[str, ExperimentState] = field(default_factory=Incomplete)
552
+
553
+ @dataclass()
554
+ class HardwareConfiguration(YamlConfig):
555
+ """This class is used to save the runtime hardware configuration parameters as a .yaml file.
556
+
557
+ This information is used to read and decode the data saved to the .npz log files during runtime as part of data
558
+ processing.
559
+
560
+ Notes:
561
+ All fields in this dataclass initialize to None. During log processing, any log associated with a hardware
562
+ module that provides the data stored in a field will be processed, unless that field is None. Therefore, setting
563
+ any field in this dataclass to None also functions as a flag for whether to parse the log associated with the
564
+ module that provides this field's information.
565
+
566
+ This class is automatically configured by MesoscopeExperiment and BehaviorTraining classes from sl-experiment
567
+ library to facilitate log parsing.
568
+ """
569
+
570
+ cue_map: dict[int, float] | None = ...
571
+ cm_per_pulse: float | None = ...
572
+ maximum_break_strength: float | None = ...
573
+ minimum_break_strength: float | None = ...
574
+ lick_threshold: int | None = ...
575
+ valve_scale_coefficient: float | None = ...
576
+ valve_nonlinearity_exponent: float | None = ...
577
+ torque_per_adc_unit: float | None = ...
578
+ screens_initially_on: bool | None = ...
579
+ recorded_mesoscope_ttl: bool | None = ...
580
+
581
+ @dataclass()
582
+ class LickTrainingDescriptor(YamlConfig):
583
+ """This class is used to save the description information specific to lick training sessions as a .yaml file.
584
+
585
+ The information stored in this class instance is filled in two steps. The main runtime function fills most fields
586
+ of the class, before it is saved as a .yaml file. After runtime, the experimenter manually fills leftover fields,
587
+ such as 'experimenter_notes,' before the class instance is transferred to the long-term storage destination.
588
+
589
+ The fully filled instance data is also used during preprocessing to write the water restriction log entry for the
590
+ trained animal.
591
+ """
592
+
593
+ experimenter: str
594
+ mouse_weight_g: float
595
+ dispensed_water_volume_ml: float
596
+ minimum_reward_delay: int
597
+ maximum_reward_delay_s: int
598
+ maximum_water_volume_ml: float
599
+ maximum_training_time_m: int
600
+ experimenter_notes: str = ...
601
+ experimenter_given_water_volume_ml: float = ...
602
+
603
+ @dataclass()
604
+ class RunTrainingDescriptor(YamlConfig):
605
+ """This class is used to save the description information specific to run training sessions as a .yaml file.
606
+
607
+ The information stored in this class instance is filled in two steps. The main runtime function fills most fields
608
+ of the class, before it is saved as a .yaml file. After runtime, the experimenter manually fills leftover fields,
609
+ such as 'experimenter_notes,' before the class instance is transferred to the long-term storage destination.
610
+
611
+ The fully filled instance data is also used during preprocessing to write the water restriction log entry for the
612
+ trained animal.
613
+ """
614
+
615
+ experimenter: str
616
+ mouse_weight_g: float
617
+ dispensed_water_volume_ml: float
618
+ final_run_speed_threshold_cm_s: float
619
+ final_run_duration_threshold_s: float
620
+ initial_run_speed_threshold_cm_s: float
621
+ initial_run_duration_threshold_s: float
622
+ increase_threshold_ml: float
623
+ run_speed_increase_step_cm_s: float
624
+ run_duration_increase_step_s: float
625
+ maximum_water_volume_ml: float
626
+ maximum_training_time_m: int
627
+ experimenter_notes: str = ...
628
+ experimenter_given_water_volume_ml: float = ...
629
+
630
+ @dataclass()
631
+ class MesoscopeExperimentDescriptor(YamlConfig):
632
+ """This class is used to save the description information specific to experiment sessions as a .yaml file.
633
+
634
+ The information stored in this class instance is filled in two steps. The main runtime function fills most fields
635
+ of the class, before it is saved as a .yaml file. After runtime, the experimenter manually fills leftover fields,
636
+ such as 'experimenter_notes,' before the class instance is transferred to the long-term storage destination.
637
+
638
+ The fully filled instance data is also used during preprocessing to write the water restriction log entry for the
639
+ animal participating in the experiment runtime.
640
+ """
641
+
642
+ experimenter: str
643
+ mouse_weight_g: float
644
+ dispensed_water_volume_ml: float
645
+ experimenter_notes: str = ...
646
+ experimenter_given_water_volume_ml: float = ...
647
+
648
+ @dataclass()
649
+ class ZaberPositions(YamlConfig):
650
+ """This class is used to save Zaber motor positions as a .yaml file to reuse them between sessions.
651
+
652
+ The class is specifically designed to store, save, and load the positions of the LickPort and HeadBar motors
653
+ (axes). It is used to both store Zaber motor positions for each session for future analysis and to restore the same
654
+ Zaber motor positions across consecutive runtimes for the same project and animal combination.
655
+
656
+ Notes:
657
+ All positions are saved using native motor units. All class fields initialize to default placeholders that are
658
+ likely NOT safe to apply to the VR system. Do not apply the positions loaded from the file unless you are
659
+ certain they are safe to use.
660
+
661
+ Exercise caution when working with Zaber motors. The motors are powerful enough to damage the surrounding
662
+ equipment and manipulated objects. Do not modify the data stored inside the .yaml file unless you know what you
663
+ are doing.
664
+ """
665
+
666
+ headbar_z: int = ...
667
+ headbar_pitch: int = ...
668
+ headbar_roll: int = ...
669
+ lickport_z: int = ...
670
+ lickport_x: int = ...
671
+ lickport_y: int = ...
672
+
673
+ @dataclass()
674
+ class MesoscopePositions(YamlConfig):
675
+ """This class is used to save the real and virtual Mesoscope objective positions as a .yaml file to reuse it
676
+ between experiment sessions.
677
+
678
+ Primarily, the class is used to help the experimenter to position the Mesoscope at the same position across
679
+ multiple imaging sessions. It stores both the physical (real) position of the objective along the motorized
680
+ X, Y, Z, and Roll axes and the virtual (ScanImage software) tip, tilt, and fastZ focus axes.
681
+
682
+ Notes:
683
+ Since the API to read and write these positions automatically is currently not available, this class relies on
684
+ the experimenter manually entering all positions and setting the mesoscope to these positions when necessary.
685
+ """
686
+
687
+ mesoscope_x_position: float = ...
688
+ mesoscope_y_position: float = ...
689
+ mesoscope_roll_position: float = ...
690
+ mesoscope_z_position: float = ...
691
+ mesoscope_fast_z_position: float = ...
692
+ mesoscope_tip_position: float = ...
693
+ mesoscope_tilt_position: float = ...
694
+
695
+ @dataclass()
696
+ class SubjectData:
697
+ """Stores the ID information of the surgical intervention's subject (animal)."""
698
+
699
+ id: int
700
+ ear_punch: str
701
+ sex: str
702
+ genotype: str
703
+ date_of_birth_us: int
704
+ weight_g: float
705
+ cage: int
706
+ location_housed: str
707
+ status: str
708
+
709
+ @dataclass()
710
+ class ProcedureData:
711
+ """Stores the general information about the surgical intervention."""
712
+
713
+ surgery_start_us: int
714
+ surgery_end_us: int
715
+ surgeon: str
716
+ protocol: str
717
+ surgery_notes: str
718
+ post_op_notes: str
719
+ surgery_quality: int = ...
720
+
721
+ @dataclass
722
+ class ImplantData:
723
+ """Stores the information about a single implantation performed during the surgical intervention.
724
+
725
+ Multiple ImplantData instances are used at the same time if the surgery involved multiple implants.
726
+ """
727
+
728
+ implant: str
729
+ implant_target: str
730
+ implant_code: int
731
+ implant_ap_coordinate_mm: float
732
+ implant_ml_coordinate_mm: float
733
+ implant_dv_coordinate_mm: float
734
+
735
+ @dataclass
736
+ class InjectionData:
737
+ """Stores the information about a single injection performed during surgical intervention.
738
+
739
+ Multiple InjectionData instances are used at the same time if the surgery involved multiple injections.
740
+ """
741
+
742
+ injection: str
743
+ injection_target: str
744
+ injection_volume_nl: float
745
+ injection_code: int
746
+ injection_ap_coordinate_mm: float
747
+ injection_ml_coordinate_mm: float
748
+ injection_dv_coordinate_mm: float
749
+
750
+ @dataclass
751
+ class DrugData:
752
+ """Stores the information about all drugs administered to the subject before, during, and immediately after the
753
+ surgical intervention.
754
+ """
755
+
756
+ lactated_ringers_solution_volume_ml: float
757
+ lactated_ringers_solution_code: int
758
+ ketoprofen_volume_ml: float
759
+ ketoprofen_code: int
760
+ buprenorphine_volume_ml: float
761
+ buprenorphine_code: int
762
+ dexamethasone_volume_ml: float
763
+ dexamethasone_code: int
764
+
765
+ @dataclass
766
+ class SurgeryData(YamlConfig):
767
+ """Stores the data about a single mouse surgical intervention.
768
+
769
+ This class aggregates other dataclass instances that store specific data about the surgical procedure. Primarily, it
770
+ is used to save the data as a .yaml file to every session's raw_data directory of each animal used in every lab
771
+ project. This way, the surgery data is always stored alongside the behavior and brain activity data collected
772
+ during the session.
773
+ """
774
+
775
+ subject: SubjectData
776
+ procedure: ProcedureData
777
+ drugs: DrugData
778
+ implants: list[ImplantData]
779
+ injections: list[InjectionData]
780
+
781
+ @dataclass()
782
+ class ProcessingTracker(YamlConfig):
783
+ """Tracks the data processing status for a single session.
784
+
785
+ This class is used during BioHPC-server data processing runtimes to track which processing steps are enabled and
786
+ have been successfully applied to a given session. This is used to optimize data processing and avoid unnecessary
787
+ processing step repetitions where possible.
788
+
789
+ Notes:
790
+ This class uses a similar mechanism for determining whether a particular option is enabled as the
791
+ HardwareConfiguration class. Specifically, if any field of the class is set to None (null), the processing
792
+ associated with that field is disabled. Otherwise, if the field is False, that session has not been processed
793
+ and, if True, the session has been processed.
794
+ """
795
+
796
+ checksum: bool | None = ...
797
+ log_extractions: bool | None = ...
798
+ suite2p: bool | None = ...
799
+ deeplabcut: bool | None = ...