Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
20 changes: 11 additions & 9 deletions src/jabs_postprocess/utils/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,17 @@ class ClassifierSettings:
def __init__(
self,
behavior: str,
interpolate: int = DEFAULT_INTERPOLATE,
stitch: int = DEFAULT_STITCH,
min_bout: int = DEFAULT_MIN_BOUT,
interpolate: int | None = None,
stitch: int | None = None,
min_bout: int | None = None,
):
"""Initializes a settings object.

Args:
behavior: string containing the name of the behavior
interpolate: number of frames where predictions will be interpolated when data is missing
stitch: number of frames between "behavior" predictions that will be merged
min_bout: minimum number of frames for "behavior" predictions to remain
interpolate: number of frames where predictions will be interpolated when data is missing (None uses default)
stitch: number of frames between "behavior" predictions that will be merged (None uses default)
min_bout: minimum number of frames for "behavior" predictions to remain (None uses default)

Todo:
Add back in the functionality to change thresholds (useful for searching for data which has poor/odd probabilities).
Expand All @@ -45,9 +45,11 @@ def __init__(
threshold_max: high threshold for calling behavior (default 1.0)
"""
self._behavior = behavior
self._interpolate = interpolate
self._stitch = stitch
self._min_bout = min_bout
self._interpolate = (
interpolate if interpolate is not None else DEFAULT_INTERPOLATE
)
self._stitch = stitch if stitch is not None else DEFAULT_STITCH
self._min_bout = min_bout if min_bout is not None else DEFAULT_MIN_BOUT

@property
def behavior(self):
Expand Down
47 changes: 47 additions & 0 deletions tests/test_generate_behavior_tables.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,3 +770,50 @@ def test_merge_multiple_behavior_tables_empty_group():

with pytest.raises(ValueError, match="No tables provided for behavior: grooming"):
merge_multiple_behavior_tables(table_groups)


# Integration tests without mocks


def test_classifier_settings_with_none_values_integration():
"""Integration test: ClassifierSettings handles None values correctly.

This is an integration test for issue #45 that verifies the fix works
in the actual codebase without mocking. It tests that None values passed
to ClassifierSettings are correctly converted to defaults and can be
used in comparison operations without raising TypeError.

This test complements the unit tests in test_metadata.py by verifying
the fix works in the context where it was actually failing.
"""
from jabs_postprocess.utils.metadata import (
ClassifierSettings,
DEFAULT_INTERPOLATE,
DEFAULT_STITCH,
DEFAULT_MIN_BOUT,
)
from jabs_postprocess.utils.project_utils import Bouts

# Create settings with None values (simulates the bug scenario)
settings = ClassifierSettings(
behavior="grooming",
interpolate=None,
stitch=None,
min_bout=None,
)

# Verify defaults are applied
assert settings.interpolate == DEFAULT_INTERPOLATE
assert settings.stitch == DEFAULT_STITCH
assert settings.min_bout == DEFAULT_MIN_BOUT

# Create some bout data to test filter_by_settings
# This simulates the actual code path where the bug occurred
bouts = Bouts.from_value_vector([0, 0, 1, 1, 1, 0, 0, -1, -1, 0, 1, 1])

# This should not raise TypeError
# Previously would fail with: TypeError: '>' not supported between instances of 'NoneType' and 'int'
try:
bouts.filter_by_settings(settings)
except TypeError as e:
pytest.fail(f"filter_by_settings raised TypeError with None values: {e}")
Loading