Skip to content

Commit

Permalink
[Pydantic II] Change all interfaces to pydantic types (#1017)
Browse files Browse the repository at this point in the history
Authored-by: CodyCBakerPhD <[email protected]>
  • Loading branch information
CodyCBakerPhD authored Aug 21, 2024
1 parent 60ca62f commit beb48d9
Show file tree
Hide file tree
Showing 73 changed files with 308 additions and 271 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
* Changed the spikeinterface.tool functions (e.g. `add_recording`, `add_sorting`) to have `_to_nwbfile` as suffix [PR #1015](https://github.com/catalystneuro/neuroconv/pull/1015)
* Deprecated use of `compression` and `compression_options` in `VideoInterface` [PR #1005](https://github.com/catalystneuro/neuroconv/pull/1005)
* `get_schema_from_method_signature` has been deprecated; please use `get_json_schema_from_method_signature` instead. [PR #1016](https://github.com/catalystneuro/neuroconv/pull/1016)
* `neuroconv.utils.FilePathType` and `neuroconv.utils.FolderPathType` have been deprecated; please use `pydantic.FilePath` and `pydantic.DirectoryPath` instead. [PR #1017](https://github.com/catalystneuro/neuroconv/pull/1017)

### Features
* Added MedPCInterface for operant behavioral output files. [PR #883](https://github.com/catalystneuro/neuroconv/pull/883)
Expand All @@ -30,6 +31,7 @@
* Add tqdm with warning to DeepLabCut interface [PR #1006](https://github.com/catalystneuro/neuroconv/pull/1006)
* `BaseRecordingInterface` now calls default metadata when metadata is not passing mimicking `run_conversion` behavior. [PR #1012](https://github.com/catalystneuro/neuroconv/pull/1012)
* Added `get_json_schema_from_method_signature` which constructs Pydantic models automatically from the signature of any function with typical annotation types used throughout NeuroConv. [PR #1016](https://github.com/catalystneuro/neuroconv/pull/1016)
* Replaced all interface annotations with Pydantic types. [PR #1017](https://github.com/catalystneuro/neuroconv/pull/1017)



Expand Down
11 changes: 6 additions & 5 deletions src/neuroconv/basedatainterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import Literal, Optional, Tuple, Union

from jsonschema.validators import validate
from pydantic import FilePath
from pynwb import NWBFile

from .tools.nwb_helpers import (
Expand Down Expand Up @@ -42,10 +43,6 @@ def __init__(self, verbose: bool = False, **source_data):
self.verbose = verbose
self.source_data = source_data

def get_conversion_options_schema(self) -> dict:
"""Infer the JSON schema for the conversion options from the method signature (annotation typing)."""
return get_json_schema_from_method_signature(self.add_to_nwbfile, exclude=["nwbfile", "metadata"])

def get_metadata_schema(self) -> dict:
"""Retrieve JSON schema for metadata."""
metadata_schema = load_dict_from_file(Path(__file__).parent / "schemas" / "base_metadata_schema.json")
Expand Down Expand Up @@ -79,6 +76,10 @@ def validate_metadata(self, metadata: dict, append_mode: bool = False) -> None:

validate(instance=decoded_metadata, schema=metdata_schema)

def get_conversion_options_schema(self) -> dict:
"""Infer the JSON schema for the conversion options from the method signature (annotation typing)."""
return get_json_schema_from_method_signature(self.add_to_nwbfile, exclude=["nwbfile", "metadata"])

def create_nwbfile(self, metadata: Optional[dict] = None, **conversion_options) -> NWBFile:
"""
Create and return an in-memory pynwb.NWBFile object with this interface's data added to it.
Expand Down Expand Up @@ -121,7 +122,7 @@ def add_to_nwbfile(self, nwbfile: NWBFile, **conversion_options) -> None:

def run_conversion(
self,
nwbfile_path: str,
nwbfile_path: FilePath,
nwbfile: Optional[NWBFile] = None,
metadata: Optional[dict] = None,
overwrite: bool = False,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

import numpy as np
import scipy
from pydantic import FilePath
from pynwb import NWBFile

from ....basetemporalalignmentinterface import BaseTemporalAlignmentInterface
Expand All @@ -27,7 +28,7 @@ class AudioInterface(BaseTemporalAlignmentInterface):
associated_suffixes = (".wav",)
info = "Interface for writing audio recordings to an NWB file."

def __init__(self, file_paths: list, verbose: bool = False):
def __init__(self, file_paths: List[FilePath], verbose: bool = False):
"""
Data interface for writing acoustic recordings to an NWB file.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@
import numpy as np
import pandas as pd
import yaml
from pydantic import FilePath
from pynwb import NWBFile
from ruamel.yaml import YAML

from ....utils import FilePathType


def _read_config(config_file_path):
"""
Expand Down Expand Up @@ -303,9 +302,9 @@ def _write_pes_to_nwbfile(

def add_subject_to_nwbfile(
nwbfile: NWBFile,
h5file: FilePathType,
h5file: FilePath,
individual_name: str,
config_file: FilePathType,
config_file: FilePath,
timestamps: Optional[Union[List, np.ndarray]] = None,
pose_estimation_container_kwargs: Optional[dict] = None,
) -> NWBFile:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
from typing import List, Optional, Union

import numpy as np
from pydantic import FilePath
from pynwb.file import NWBFile

from ....basetemporalalignmentinterface import BaseTemporalAlignmentInterface
from ....utils import FilePathType


class DeepLabCutInterface(BaseTemporalAlignmentInterface):
Expand All @@ -27,8 +27,8 @@ def get_source_schema(cls) -> dict:

def __init__(
self,
file_path: FilePathType,
config_file_path: FilePathType,
file_path: FilePath,
config_file_path: FilePath,
subject_name: str = "ind1",
verbose: bool = True,
):
Expand All @@ -37,9 +37,9 @@ def __init__(
Parameters
----------
file_path : FilePathType
file_path : FilePath
path to the h5 file output by dlc.
config_file_path : FilePathType
config_file_path : FilePath
path to .yml config file
subject_name : str, default: "ind1"
the name of the subject for which the :py:class:`~pynwb.file.NWBFile` is to be created.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
from typing import Optional, Union

import numpy as np
from pydantic import FilePath
from pynwb.behavior import Position, SpatialSeries
from pynwb.file import NWBFile

from ....basetemporalalignmentinterface import BaseTemporalAlignmentInterface
from ....tools import get_module
from ....utils import FilePathType, calculate_regular_series_rate
from ....utils import calculate_regular_series_rate


class FicTracDataInterface(BaseTemporalAlignmentInterface):
Expand Down Expand Up @@ -155,22 +156,22 @@ def get_source_schema(cls) -> dict:

def __init__(
self,
file_path: FilePathType,
file_path: FilePath,
radius: Optional[float] = None,
configuration_file_path: Optional[FilePathType] = None,
configuration_file_path: Optional[FilePath] = None,
verbose: bool = True,
):
"""
Interface for writing FicTrac files to nwb.
Parameters
----------
file_path : a string or a path
file_path : FilePath
Path to the .dat file (the output of fictrac)
radius : float, optional
The radius of the ball in meters. If provided the radius is stored as a conversion factor
and the units are set to meters. If not provided the units are set to radians.
configuration_file_path : a string or a path, optional
configuration_file_path : FilePath, optional
Path to the .txt file with the configuration metadata. Usually called config.txt
verbose : bool, default: True
controls verbosity. ``True`` by default.
Expand Down Expand Up @@ -358,8 +359,8 @@ def set_aligned_starting_time(self, aligned_starting_time):


def extract_session_start_time(
file_path: FilePathType,
configuration_file_path: Optional[FilePathType] = None,
file_path: FilePath,
configuration_file_path: Optional[FilePath] = None,
) -> Union[datetime, None]:
"""
Extract the session start time from a FicTrac data file or its configuration file.
Expand All @@ -378,9 +379,9 @@ def extract_session_start_time(
Parameters
----------
file_path : FilePathType
file_path : FilePath
Path to the FicTrac data file.
configuration_file_path : Optional[FilePathType]
configuration_file_path : FilePath, optional
Path to the FicTrac configuration file. If omitted, the function defaults to searching for
"fictrac_config.txt" in the same directory as the data file.
Expand Down Expand Up @@ -413,13 +414,13 @@ def extract_session_start_time(
return None


def parse_fictrac_config(file_path: FilePathType) -> dict:
def parse_fictrac_config(file_path: FilePath) -> dict:
"""
Parse a FicTrac configuration file and return a dictionary of its parameters.
Parameters
----------
file_path : str, Path
file_path : FilePath
Path to the configuration file in txt format.
Returns
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
from copy import deepcopy
from typing import List, Optional

from pydantic import FilePath
from pynwb import NWBFile

from neuroconv import NWBConverter
from neuroconv.datainterfaces import LightningPoseDataInterface, VideoInterface
from neuroconv.tools.nwb_helpers import make_or_load_nwbfile
from neuroconv.utils import (
DeepDict,
FilePathType,
dict_deep_update,
get_schema_from_method_signature,
)
Expand All @@ -28,9 +28,9 @@ def get_source_schema(cls):

def __init__(
self,
file_path: FilePathType,
original_video_file_path: FilePathType,
labeled_video_file_path: Optional[FilePathType] = None,
file_path: FilePath,
original_video_file_path: FilePath,
labeled_video_file_path: Optional[FilePath] = None,
image_series_original_video_name: Optional[str] = None,
image_series_labeled_video_name: Optional[str] = None,
verbose: bool = True,
Expand All @@ -41,11 +41,11 @@ def __init__(
Parameters
----------
file_path : a string or a path
file_path : FilePath
Path to the .csv file that contains the predictions from Lightning Pose.
original_video_file_path : a string or a path
original_video_file_path : FilePath
Path to the original video file (.mp4).
labeled_video_file_path : a string or a path, optional
labeled_video_file_path : FilePath, optional
Path to the labeled video file (.mp4).
image_series_original_video_name: string, optional
The name of the ImageSeries to add for the original video.
Expand Down Expand Up @@ -160,7 +160,7 @@ def add_to_nwbfile(

def run_conversion(
self,
nwbfile_path: Optional[str] = None,
nwbfile_path: Optional[FilePath] = None,
nwbfile: Optional[NWBFile] = None,
metadata: Optional[dict] = None,
overwrite: bool = False,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
from typing import Optional, Tuple

import numpy as np
from pydantic import FilePath
from pynwb import NWBFile

from ....basetemporalalignmentinterface import BaseTemporalAlignmentInterface
from ....tools import get_module
from ....utils import (
DeepDict,
FilePathType,
calculate_regular_series_rate,
get_base_schema,
)
Expand Down Expand Up @@ -60,19 +60,19 @@ def get_metadata_schema(self) -> dict:

def __init__(
self,
file_path: FilePathType,
original_video_file_path: FilePathType,
labeled_video_file_path: Optional[FilePathType] = None,
file_path: FilePath,
original_video_file_path: FilePath,
labeled_video_file_path: Optional[FilePath] = None,
verbose: bool = True,
):
"""
Interface for writing pose estimation data from the Lightning Pose algorithm.
Parameters
----------
file_path : a string or a path
file_path : FilePath
Path to the .csv file that contains the predictions from Lightning Pose.
original_video_file_path : a string or a path
original_video_file_path : FilePath
Path to the original video file (.mp4).
labeled_video_file_path : a string or a path, optional
Path to the labeled video file (.mp4).
Expand Down
7 changes: 3 additions & 4 deletions src/neuroconv/datainterfaces/behavior/medpc/medpc_helpers.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import numpy as np
from pydantic import FilePath

from neuroconv.utils import FilePathType


def get_medpc_variables(file_path: FilePathType, variable_names: list) -> dict:
def get_medpc_variables(file_path: FilePath, variable_names: list) -> dict:
"""
Get the values of the given single-line variables from a MedPC file for all sessions in that file.
Expand Down Expand Up @@ -85,7 +84,7 @@ def _get_session_lines(lines: list, session_conditions: dict, start_variable: st


def read_medpc_file(
file_path: FilePathType,
file_path: FilePath,
medpc_name_to_info_dict: dict,
session_conditions: dict,
start_variable: str,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
from typing import Optional

import numpy as np
from pydantic import FilePath
from pynwb.behavior import BehavioralEpochs, IntervalSeries
from pynwb.file import NWBFile

from neuroconv.basetemporalalignmentinterface import BaseTemporalAlignmentInterface
from neuroconv.tools import get_package, nwb_helpers
from neuroconv.utils import DeepDict, FilePathType
from neuroconv.utils import DeepDict

from .medpc_helpers import read_medpc_file

Expand Down Expand Up @@ -42,7 +43,7 @@ class MedPCInterface(BaseTemporalAlignmentInterface):

def __init__(
self,
file_path: FilePathType,
file_path: FilePath,
session_conditions: dict,
start_variable: str,
metadata_medpc_name_to_info_dict: dict,
Expand All @@ -54,7 +55,7 @@ def __init__(
Parameters
----------
file_path : FilePathType
file_path : FilePath
Path to the MedPC file.
session_conditions : dict
The conditions that define the session. The keys are the names of the single-line variables (ex. 'Start Date')
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from pathlib import Path

from pydantic import DirectoryPath
from pynwb import NWBFile

from .... import BaseDataInterface
from ....tools import get_package
from ....utils import DeepDict, FolderPathType
from ....utils import DeepDict


class MiniscopeBehaviorInterface(BaseDataInterface):
Expand All @@ -23,13 +24,13 @@ def get_source_schema(cls) -> dict:
] = "The main Miniscope folder. The movie files are expected to be in sub folders within the main folder."
return source_schema

def __init__(self, folder_path: FolderPathType):
def __init__(self, folder_path: DirectoryPath):
"""
Initialize reading recordings from the Miniscope behavioral camera.
Parameters
----------
folder_path : FolderPathType
folder_path : DirectoryPath
The path that points to the main Miniscope folder.
The movie files are expected to be in sub folders within the main folder.
"""
Expand Down
Loading

0 comments on commit beb48d9

Please sign in to comment.