Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using ruff rule to enforce the existence of docstrings in public methods #1063

Open
wants to merge 23 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
# Upcoming

## Deprecations

## Bug Fixes

## Deprecations

## Features
* Using in-house `GenericDataChunkIterator` [PR #1068](https://github.com/catalystneuro/neuroconv/pull/1068)

## Improvements
* Run only the most basic testing while a PR is on draft [PR #1082](https://github.com/catalystneuro/neuroconv/pull/1082)
* Using ruff to enforce existence of public functions's docstrings [PR #1062](https://github.com/catalystneuro/neuroconv/pull/1062)

# v0.6.4 (September 17, 2024)

Expand Down
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ doctest_optionflags = "ELLIPSIS"

[tool.black]
line-length = 120
target-version = ['py38', 'py39', 'py310']
target-version = ['py39', 'py310']
include = '\.pyi?$'
extend-exclude = '''
/(
Expand All @@ -131,6 +131,7 @@ select = [
"F401", # Unused import
"I", # All isort rules
"D101", # Missing docstring in public class
"D102", # Missing docstring in public method
"D103", # Missing docstring in public function
]
fixable = ["ALL"]
Expand Down
2 changes: 1 addition & 1 deletion src/neuroconv/baseextractorinterface.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class BaseExtractorInterface(BaseTemporalAlignmentInterface, ABC):
Extractor = None # Class loads dynamically on first call to .get_extractor()

@classmethod
def get_extractor(cls):
def get_extractor(cls): # noqa: D102
if cls.Extractor is not None:
return cls.Extractor
extractor_module = get_package(package_name=cls.ExtractorModuleName)
Expand Down
14 changes: 7 additions & 7 deletions src/neuroconv/datainterfaces/behavior/audio/audiointerface.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def __init__(self, file_paths: list[FilePath], verbose: bool = False):
super().__init__(file_paths=file_paths)
self._segment_starting_times = None

def get_metadata_schema(self) -> dict:
def get_metadata_schema(self) -> dict: # noqa: D102
metadata_schema = super().get_metadata_schema()

time_series_metadata_schema_path = (
Expand All @@ -84,7 +84,7 @@ def get_metadata_schema(self) -> dict:
)
return metadata_schema

def get_metadata(self) -> dict:
def get_metadata(self) -> dict: # noqa: D102
default_name = "AcousticWaveformSeries"
is_multiple_file_path = len(self.source_data["file_paths"]) > 1
audio_metadata = [
Expand All @@ -96,17 +96,17 @@ def get_metadata(self) -> dict:
]
behavior_metadata = dict(Audio=audio_metadata)

metadata = super().get_metadata()
metadata = super().get_metadata() # noqa: D102
metadata.update(Behavior=behavior_metadata)
return metadata

def get_original_timestamps(self) -> np.ndarray:
def get_original_timestamps(self) -> np.ndarray: # noqa: D102
raise NotImplementedError("The AudioInterface does not yet support timestamps.")

def get_timestamps(self) -> Optional[np.ndarray]:
def get_timestamps(self) -> Optional[np.ndarray]: # noqa: D102
raise NotImplementedError("The AudioInterface does not yet support timestamps.")

def set_aligned_timestamps(self, aligned_timestamps: list[np.ndarray]):
def set_aligned_timestamps(self, aligned_timestamps: list[np.ndarray]): # noqa: D102
raise NotImplementedError("The AudioInterface does not yet support timestamps.")

def set_aligned_starting_time(self, aligned_starting_time: float):
Expand Down Expand Up @@ -155,7 +155,7 @@ def set_aligned_segment_starting_times(self, aligned_segment_starting_times: lis

self._segment_starting_times = aligned_segment_starting_times

def align_by_interpolation(self, unaligned_timestamps: np.ndarray, aligned_timestamps: np.ndarray):
def align_by_interpolation(self, unaligned_timestamps: np.ndarray, aligned_timestamps: np.ndarray): # noqa: D102
raise NotImplementedError("The AudioInterface does not yet support timestamps.")

def add_to_nwbfile(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class DeepLabCutInterface(BaseTemporalAlignmentInterface):
_timestamps = None

@classmethod
def get_source_schema(cls) -> dict:
def get_source_schema(cls) -> dict: # noqa: D102
source_schema = super().get_source_schema()
source_schema["properties"]["file_path"]["description"] = "Path to the .h5 file output by dlc."
source_schema["properties"]["config_file_path"]["description"] = "Path to .yml config file"
Expand Down Expand Up @@ -60,7 +60,7 @@ def __init__(
self.verbose = verbose
super().__init__(file_path=file_path, config_file_path=config_file_path)

def get_metadata(self):
def get_metadata(self): # noqa: D102
metadata = super().get_metadata()

if self.config_dict:
Expand All @@ -71,13 +71,13 @@ def get_metadata(self):

return metadata

def get_original_timestamps(self) -> np.ndarray:
def get_original_timestamps(self) -> np.ndarray: # noqa: D102
raise NotImplementedError(
"Unable to retrieve the original unaltered timestamps for this interface! "
"Define the `get_original_timestamps` method for this interface."
)

def get_timestamps(self) -> np.ndarray:
def get_timestamps(self) -> np.ndarray: # noqa: D102
raise NotImplementedError(
"Unable to retrieve timestamps for this interface! Define the `get_timestamps` method for this interface."
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ class FicTracDataInterface(BaseTemporalAlignmentInterface):
}

@classmethod
def get_source_schema(cls) -> dict:
def get_source_schema(cls) -> dict: # noqa: D102
source_schema = super().get_source_schema()
source_schema["properties"]["file_path"]["description"] = "Path to the .dat file (the output of fictrac)"
return source_schema
Expand Down Expand Up @@ -194,7 +194,7 @@ def __init__(
self._timestamps = None
self._starting_time = None

def get_metadata(self):
def get_metadata(self): # noqa: D102
metadata = super().get_metadata()

session_start_time = extract_session_start_time(
Expand Down Expand Up @@ -345,17 +345,17 @@ def get_original_timestamps(self):

return timestamps

def get_timestamps(self):
def get_timestamps(self): # noqa: D102
timestamps = self._timestamps if self._timestamps is not None else self.get_original_timestamps()
if self._starting_time is not None:
timestamps = timestamps + self._starting_time

return timestamps

def set_aligned_timestamps(self, aligned_timestamps):
def set_aligned_timestamps(self, aligned_timestamps): # noqa: D102
self._timestamps = aligned_timestamps

def set_aligned_starting_time(self, aligned_starting_time):
def set_aligned_starting_time(self, aligned_starting_time): # noqa: D102
self._starting_time = aligned_starting_time


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class LightningPoseConverter(NWBConverter):
info = "Interface for handling multiple streams of lightning pose data."

@classmethod
def get_source_schema(cls):
def get_source_schema(cls): # noqa: D102
return get_schema_from_method_signature(cls)

@validate_call
Expand Down Expand Up @@ -70,14 +70,14 @@ def __init__(
self.labeled_video_name = image_series_labeled_video_name or "ImageSeriesLabeledVideo"
self.data_interface_objects.update(dict(LabeledVideo=VideoInterface(file_paths=[labeled_video_file_path])))

def get_conversion_options_schema(self) -> dict:
def get_conversion_options_schema(self) -> dict: # noqa: D102
conversion_options_schema = get_schema_from_method_signature(
method=self.add_to_nwbfile, exclude=["nwbfile", "metadata"]
)

return conversion_options_schema

def get_metadata(self) -> DeepDict:
def get_metadata(self) -> DeepDict: # noqa: D102
metadata = self.data_interface_objects["PoseEstimation"].get_metadata()
original_video_interface = self.data_interface_objects["OriginalVideo"]
original_videos_metadata = original_video_interface.get_metadata()
Expand Down Expand Up @@ -111,6 +111,28 @@ def add_to_nwbfile(
starting_frames_labeled_videos: Optional[list[int]] = None,
stub_test: bool = False,
):
"""
Add behavior and pose estimation data, including original and labeled videos, to the specified NWBFile.

Parameters
----------
nwbfile : NWBFile
The NWBFile object to which the data will be added.
metadata : dict
Metadata dictionary containing information about the behavior and videos.
reference_frame : str, optional
Description of the reference frame for pose estimation, by default None.
confidence_definition : str, optional
Definition for the confidence levels in pose estimation, by default None.
external_mode : bool, optional
If True, the videos will be referenced externally rather than embedded within the NWB file, by default True.
starting_frames_original_videos : list of int, optional
List of starting frames for the original videos, by default None.
starting_frames_labeled_videos : list of int, optional
List of starting frames for the labeled videos, by default None.
stub_test : bool, optional
If True, only a subset of the data will be added for testing purposes, by default False.
"""
original_video_interface = self.data_interface_objects["OriginalVideo"]

original_video_metadata = next(
Expand Down Expand Up @@ -172,6 +194,33 @@ def run_conversion(
starting_frames_labeled_videos: Optional[list] = None,
stub_test: bool = False,
) -> None:
"""
Run the full conversion process, adding behavior, video, and pose estimation data to an NWB file.

Parameters
----------
nwbfile_path : FilePath, optional
The file path where the NWB file will be saved. If None, the file is handled in memory.
nwbfile : NWBFile, optional
An in-memory NWBFile object. If None, a new NWBFile object will be created.
metadata : dict, optional
Metadata dictionary for describing the NWB file contents. If None, it is auto-generated.
overwrite : bool, optional
If True, overwrites the NWB file at `nwbfile_path` if it exists. If False, appends to the file, by default False.
reference_frame : str, optional
Description of the reference frame for pose estimation, by default None.
confidence_definition : str, optional
Definition for confidence levels in pose estimation, by default None.
external_mode : bool, optional
If True, the videos will be referenced externally rather than embedded within the NWB file, by default True.
starting_frames_original_videos : list of int, optional
List of starting frames for the original videos, by default None.
starting_frames_labeled_videos : list of int, optional
List of starting frames for the labeled videos, by default None.
stub_test : bool, optional
If True, only a subset of the data will be added for testing purposes, by default False.

"""
if metadata is None:
metadata = self.get_metadata()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class LightningPoseDataInterface(BaseTemporalAlignmentInterface):
associated_suffixes = (".csv", ".mp4")
info = "Interface for handling a single stream of lightning pose data."

def get_metadata_schema(self) -> dict:
def get_metadata_schema(self) -> dict: # noqa: D102
metadata_schema = super().get_metadata_schema()
metadata_schema["properties"]["Behavior"] = get_base_schema(tag="Behavior")

Expand Down Expand Up @@ -123,24 +123,24 @@ def _get_original_video_shape(self) -> tuple[int, int]:
# image size of the original video is in height x width
return video_shape[0], video_shape[1]

def get_original_timestamps(self, stub_test: bool = False) -> np.ndarray:
def get_original_timestamps(self, stub_test: bool = False) -> np.ndarray: # noqa: D102
max_frames = 10 if stub_test else None
with self._vc(file_path=str(self.original_video_file_path)) as video:
timestamps = video.get_video_timestamps(max_frames=max_frames)
return timestamps

def get_timestamps(self, stub_test: bool = False) -> np.ndarray:
def get_timestamps(self, stub_test: bool = False) -> np.ndarray: # noqa: D102
max_frames = 10 if stub_test else None
if self._times is None:
return self.get_original_timestamps(stub_test=stub_test)

timestamps = self._times if not stub_test else self._times[:max_frames]
return timestamps

def set_aligned_timestamps(self, aligned_timestamps: np.ndarray):
def set_aligned_timestamps(self, aligned_timestamps: np.ndarray): # noqa: D102
self._times = aligned_timestamps

def get_metadata(self) -> DeepDict:
def get_metadata(self) -> DeepDict: # noqa: D102
metadata = super().get_metadata()

# Update the session start time if folder structure is saved in the format: YYYY-MM-DD/HH-MM-SS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def __init__(
)
self.timestamps_dict = {}

def get_metadata(self) -> DeepDict:
def get_metadata(self) -> DeepDict: # noqa: D102
metadata = super().get_metadata()
session_dict = read_medpc_file(
file_path=self.source_data["file_path"],
Expand All @@ -98,7 +98,7 @@ def get_metadata(self) -> DeepDict:

return metadata

def get_metadata_schema(self) -> dict:
def get_metadata_schema(self) -> dict: # noqa: D102
metadata_schema = super().get_metadata_schema()
medpc_name_to_info_dict = self.source_data["metadata_medpc_name_to_info_dict"]
metadata_schema["properties"]["MedPC"] = {
Expand Down Expand Up @@ -178,11 +178,12 @@ def set_aligned_starting_time(self, aligned_starting_time: float, medpc_name_to_
aligned_timestamps_dict[name] = original_timestamps + aligned_starting_time
self.set_aligned_timestamps(aligned_timestamps_dict=aligned_timestamps_dict)

def add_to_nwbfile(
def add_to_nwbfile( # noqa: D102
self,
nwbfile: NWBFile,
metadata: dict,
) -> None:

ndx_events = get_package(package_name="ndx_events", installation_instructions="pip install ndx-events")
medpc_name_to_info_dict = metadata["MedPC"].get("medpc_name_to_info_dict", None)
assert medpc_name_to_info_dict is not None, "medpc_name_to_info_dict must be provided in metadata"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ class MiniscopeBehaviorInterface(BaseDataInterface):
info = "Interface for Miniscope behavior video data."

@classmethod
def get_source_schema(cls) -> dict:
def get_source_schema(cls) -> dict: # noqa: D102
source_schema = super().get_source_schema()
source_schema["properties"]["folder_path"][
"description"
Expand Down Expand Up @@ -66,7 +66,7 @@ def __init__(self, folder_path: DirectoryPath):
assert len(self._starting_frames) == len(self._behav_avi_file_paths)
self._timestamps = get_timestamps(folder_path=str(folder_path), file_pattern="BehavCam*/timeStamps.csv")

def get_metadata(self) -> DeepDict:
def get_metadata(self) -> DeepDict: # noqa: D102
metadata = super().get_metadata()
metadata["NWBFile"].update(session_start_time=self._recording_start_times[0])

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,21 +41,21 @@ def __init__(self, file_path: FilePath, verbose: bool = True):

super().__init__(file_path=file_path)

def get_original_timestamps(self) -> np.ndarray:
def get_original_timestamps(self) -> np.ndarray: # noqa: D102
data = read_data(self.file_path)

times = data["TimeStamp"] / 1000000 # Neuralynx stores times in microseconds
times = times - times[0]

return times

def get_timestamps(self) -> np.ndarray:
def get_timestamps(self) -> np.ndarray: # noqa: D102
return self._timestamps

def set_aligned_timestamps(self, aligned_timestamps: np.ndarray) -> None:
def set_aligned_timestamps(self, aligned_timestamps: np.ndarray) -> None: # noqa: D102
self._timestamps = aligned_timestamps

def get_metadata(self) -> DeepDict:
def get_metadata(self) -> DeepDict: # noqa: D102
metadata = super().get_metadata()

metadata["NWBFile"].update(session_start_time=self.header["TimeCreated"])
Expand All @@ -67,7 +67,7 @@ def get_metadata(self) -> DeepDict:
)
return metadata

def get_metadata_schema(self) -> dict:
def get_metadata_schema(self) -> dict: # noqa: D102
metadata_schema = super().get_metadata_schema()

if "Behavior" not in metadata_schema["properties"]:
Expand Down
Loading
Loading