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

feat: Add BaseWriter, and example NIFTIWriter with notebooks documenting them #84

Merged
merged 15 commits into from
Dec 13, 2024

Conversation

jjjermiah
Copy link
Contributor

@jjjermiah jjjermiah commented Dec 12, 2024

Mostly inspired and reusing logic from Med-ImageTools DICOMSorter design

  • I will very likely look to extract the BaseWriter stuff into MIT later so it can be reused around, but worth going through a review process

Summary by CodeRabbit

  • New Features

    • Enhanced Jupyter notebooks for saving medical imaging data in NIFTI format and CSV metadata.
    • Added examples for using subclasses of BaseWriter for writing text and CSV files.
    • Introduced the NIFTIWriter class for managing NIFTI file writing with validation and error handling.
    • Added new CSVWriter class for saving data in CSV format.
  • Documentation

    • Added comprehensive documentation for the Writer system, including details on BaseWriter and its subclasses.
    • Updated module docstrings for better clarity on their purpose.
  • Bug Fixes

    • Improved error handling for file writing operations.
  • Chores

    • Adjusted linting configuration to include and exclude specific files and directories.

Copy link
Contributor

coderabbitai bot commented Dec 12, 2024

Warning

Rate limit exceeded

@jjjermiah has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 22 minutes and 48 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between fb82114 and c806934.

⛔ Files ignored due to path filters (1)
  • pixi.lock is excluded by !**/*.lock
📒 Files selected for processing (1)
  • config/ruff.toml (2 hunks)

Walkthrough

The pull request introduces modifications across multiple files, focusing on the configuration of linting rules in ruff.toml, the addition of new Jupyter notebooks for writing data, and enhancements to the BaseWriter system for file management. Key changes include the addition of new classes and methods for handling NIFTI and CSV file writing, updates to documentation, and the introduction of error handling mechanisms. These changes collectively enhance the functionality and usability of the data writing tools within the project.

Changes

File Change Summary
config/ruff.toml - Added entry for Python files in src/readii/io/writers/ to include list.
- Updated extend-exclude to include notebooks/* and reformatted src/readii/pipeline.py.
notebooks/nifti_writer_example.ipynb - Introduced nifti_writer_example.ipynb for saving medical imaging data in NIFTI format, including a custom CSVWriter class with a save method.
notebooks/writer_examples.ipynb - Created writer_examples.ipynb demonstrating the use of TextWriter and CSVWriter classes for writing text and CSV files, including their respective save methods.
src/readii/io/init.py - Added docstring: "Tools for reading and writing data."
src/readii/io/writers/README.md - New documentation for BaseWriter and its subclasses, detailing architecture, methods, and usage examples.
src/readii/io/writers/init.py - Added docstring: "Tools for writing data."
src/readii/io/writers/base_writer.py - Introduced BaseWriter class with methods for pattern validation, file path resolution, and resource management.
src/readii/io/writers/nifti_writer.py - Added NIFTIWriter class extending BaseWriter, including custom exceptions and a save method for writing NIFTI files with validation checks.
src/readii/io/utils/init.py - New file to initialize the utilities module, importing PatternResolver and PatternResolverError.
src/readii/io/utils/pattern_resolver.py - Introduced PatternResolver class for handling filename pattern parsing and validation, along with the PatternResolverError exception class.
tests/io/test_base_writer.py - New tests for BaseWriter subclasses (SimpleWriter, MediumWriter, ComplexWriter) to validate their save methods and error handling.
tests/io/test_nifti_writer.py - Unit tests for NIFTIWriter class, covering validation and error handling scenarios.
tests/io/test_pattern_resolver.py - Parameterized tests for PatternResolver class to validate the resolve method under various conditions.

Possibly related PRs

Suggested reviewers

  • strixy16

🐰 In the meadow where data flows,
A writer's tale begins to grow.
With NIFTI and CSV, we save with glee,
Patterns resolved, oh what a spree!
In notebooks bright, our functions shine,
Tools for writing, all align! 🌼


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Experiment)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

codecov bot commented Dec 12, 2024

Codecov Report

Attention: Patch coverage is 91.30435% with 12 lines in your changes missing coverage. Please review.

Project coverage is 50.89%. Comparing base (c10a568) to head (c806934).
Report is 6 commits behind head on main.

Files with missing lines Patch % Lines
src/readii/io/writers/nifti_writer.py 86.53% 7 Missing ⚠️
src/readii/io/utils/pattern_resolver.py 92.30% 3 Missing ⚠️
src/readii/io/writers/base_writer.py 95.55% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #84      +/-   ##
==========================================
+ Coverage   44.51%   50.89%   +6.37%     
==========================================
  Files          16       21       +5     
  Lines         739      894     +155     
==========================================
+ Hits          329      455     +126     
- Misses        410      439      +29     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@jjjermiah jjjermiah requested a review from strixy16 December 12, 2024 15:56
@jjjermiah jjjermiah marked this pull request as ready for review December 12, 2024 15:58
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Outside diff range and nitpick comments (2)
src/readii/io/writers/nifti_writer.py (1)

70-70: Rename parameter PatientID to patient_id to follow PEP 8 naming conventions.

According to PEP 8 guidelines, parameter names should be in lowercase with words separated by underscores. Changing PatientID to patient_id improves readability and maintains consistency across the codebase.

src/readii/io/writers/README.md (1)

37-37: Correct the punctuation and formatting in the bullet point.

There's an issue with the punctuation in the bullet point for save(*args, **kwargs). It seems there's an extra period or incorrect indentation. Ensure that each bullet point is properly formatted:

-      - `resolve_path(**kwargs)`: Generates the file path by replacing placeholders in the
-        `filename_format`.    - `save(*args, **kwargs)`: Abstract method. Subclasses implement the logic for writing files.
+      - `resolve_path(**kwargs)`: Generates the file path by replacing placeholders in the `filename_format`.
+      - `save(*args, **kwargs)`: Abstract method. Subclasses implement the logic for writing files.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~37-~37: Loose punctuation mark.
Context: ...me_format. - save(*args, **kwargs)`: Abstract method. Subclasses implement t...

(UNLIKELY_OPENING_PUNCTUATION)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e868f6 and 7934c76.

⛔ Files ignored due to path filters (3)
  • notebooks/nifti_writer_example.pdf is excluded by !**/*.pdf
  • notebooks/writer_examples.pdf is excluded by !**/*.pdf
  • pixi.lock is excluded by !**/*.lock
📒 Files selected for processing (8)
  • config/ruff.toml (2 hunks)
  • notebooks/nifti_writer_example.ipynb (1 hunks)
  • notebooks/writer_examples.ipynb (1 hunks)
  • src/readii/io/__init__.py (1 hunks)
  • src/readii/io/writers/README.md (1 hunks)
  • src/readii/io/writers/__init__.py (1 hunks)
  • src/readii/io/writers/base_writer.py (1 hunks)
  • src/readii/io/writers/nifti_writer.py (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • src/readii/io/writers/init.py
  • src/readii/io/init.py
👮 Files not reviewed due to content moderation or server errors (2)
  • notebooks/nifti_writer_example.ipynb
  • notebooks/writer_examples.ipynb
🧰 Additional context used
📓 Learnings (1)
config/ruff.toml (1)
Learnt from: jjjermiah
PR: bhklab/readii#42
File: config/ruff.toml:19-21
Timestamp: 2024-11-12T13:18:03.441Z
Learning: In the 'readii' Python project, the team is incrementally adding files to the Ruff linter configuration in 'config/ruff.toml' to make small progress, rather than including all source files at once.
🪛 LanguageTool
src/readii/io/writers/README.md

[uncategorized] ~37-~37: Loose punctuation mark.
Context: ...me_format. - save(*args, **kwargs)`: Abstract method. Subclasses implement t...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (1)
config/ruff.toml (1)

8-8: Changes align with incremental updates to the linter configuration.

Including "src/readii/io/writers/**.py" in include and adding "notebooks/*" to extend-exclude is consistent with the team's approach of gradually adding files to the Ruff linter configuration. This allows for manageable progress while maintaining code quality.

Also applies to: 19-21


filename_format: str = field(init=True)

DEFAULT_PATTERN: ClassVar[re.Pattern] = re.compile(r"%(\w+)|\{(\w+)\}")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a comment breaking down what this regex is at a high level?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yup, moved to a utils directory and added more documentation throughout the module

Let me know if this is not confusing:

https://github.com/bhklab/readii/blob/cdfcdfcf1ef54cf3aeaa20d94cfaf3610f49792d/src/readii/io/utils/pattern_resolver.py

…d example would be

I know this is just a dummy example, but wanted to update based on Sejin's feedback at the R2R project update meeting.
Images now save in a modality specific directory and there's only one metadata file per patient since that's a "data_set" in this example.
Copy link
Collaborator

@strixy16 strixy16 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good!

Only concern is in how a BaseWriter subclass save function handles different types of input files to the save function - see comment in NIFTIWriter save function.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Outside diff range and nitpick comments (9)
src/readii/io/utils/pattern_resolver.py (1)

97-101: Update the Raises section in the docstring to reflect the correct exception

In the docstring of the resolve method, the Raises section mentions ValueError, but the code raises PatternResolverError. For clarity and accuracy, the docstring should be updated to specify PatternResolverError to match the actual exception being raised.

tests/io/test_pattern_resolver.py (1)

2-2: Avoid unnecessary # type: ignore comments

The # type: ignore comment suppresses type checking for this import statement. It's better to resolve the underlying type issues to benefit from static type checking. Ensure that the imported modules have proper type annotations or adjust the type hints accordingly.

tests/io/test_base_writer.py (2)

2-2: Remove unused import os

The os module is imported but not used in the code. Removing unused imports improves code readability and reduces clutter.

🧰 Tools
🪛 Ruff (0.8.2)

2-2: os imported but unused

Remove unused import: os

(F401)


79-81: Simplify with statements and remove unused variable

You can combine the nested with statements into a single with statement for better readability. Additionally, the variable file_path is assigned but never used, since the exception is expected to be raised before it's utilized. Consider removing the assignment to file_path.

Apply this diff to simplify the code:

-def test_no_create_dirs_non_existent(temp_dir):
-    with pytest.raises(FileNotFoundError):
-        with SimpleWriter(root_directory=temp_dir / "nested_non_existent", filename_format="{date_time}.txt", create_dirs=False) as writer:
-            file_path = writer.save("Content")
+def test_no_create_dirs_non_existent(temp_dir):
+    with pytest.raises(FileNotFoundError), \
+         SimpleWriter(root_directory=temp_dir / "nested_non_existent", filename_format="{date_time}.txt", create_dirs=False):
+        pass
🧰 Tools
🪛 Ruff (0.8.2)

79-80: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


81-81: Local variable file_path is assigned to but never used

Remove assignment to unused variable file_path

(F841)

tests/io/test_nifti_writer.py (3)

23-44: Add boundary test for minimum compression level.

While the tests cover invalid cases well, consider adding a test for the minimum compression level (0) to ensure complete boundary testing.

def test_min_compression_level():
    """Test for minimum compression level."""
    writer = NIFTIWriter(
        root_directory=Path("TRASH"),
        filename_format="{PatientID}.nii.gz",
        compression_level=0,  # Minimum compression level
        overwrite=False,
        create_dirs=True,
    )
    assert writer.compression_level == 0

45-61: Add test for metadata validation in saved files.

Consider adding a test to verify that the saved NIFTI file contains the expected metadata.

def test_save_with_metadata_validation(nifti_writer, sample_image):
    """Test that saved file contains expected metadata."""
    patient_id = "12345"
    out_path = nifti_writer.save(sample_image, PatientID=patient_id)
    saved_image = sitk.ReadImage(str(out_path))
    # Verify metadata preservation
    assert saved_image.GetSize() == sample_image.GetSize()
    assert saved_image.GetPixelID() == sample_image.GetPixelID()

62-80: Enhance parameterized tests with file characteristic verification.

The tests verify file existence but could be enhanced to check compression and format-specific characteristics.

@pytest.mark.parametrize("compression_level", [0, 5, 9])
def test_save_with_different_compression_levels(nifti_writer, sample_image, compression_level):
    """Test saving with different compression levels."""
    nifti_writer.compression_level = compression_level
    out_path = nifti_writer.save(sample_image, PatientID="12345")
    assert out_path.exists()
    # Verify file size characteristics
    if compression_level == 0:
        assert out_path.stat().st_size > 0  # Uncompressed should be larger
    saved_image = sitk.ReadImage(str(out_path))
    assert saved_image.GetSize() == sample_image.GetSize()
src/readii/io/writers/nifti_writer.py (2)

11-26: Enhance exception classes with error message templates.

Consider adding error message templates to make error reporting more consistent.

 class NiftiWriterValidationError(NiftiWriterError):
     """Raised when validation of writer configuration fails."""
-    pass
+    def __init__(self, message: str, *args: object) -> None:
+        super().__init__(f"Validation Error: {message}", *args)

 class NiftiWriterIOError(NiftiWriterError):
     """Raised when I/O operations fail."""
-    pass
+    def __init__(self, message: str, *args: object) -> None:
+        super().__init__(f"I/O Error: {message}", *args)

117-128: Move example code to documentation.

The example code in the main block would be better placed in the module's documentation or a dedicated examples directory.

Consider moving this to a dedicated example file or adding it to the docstring as a usage example.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc7fa16 and cdfcdfc.

📒 Files selected for processing (7)
  • src/readii/io/utils/__init__.py (1 hunks)
  • src/readii/io/utils/pattern_resolver.py (1 hunks)
  • src/readii/io/writers/base_writer.py (1 hunks)
  • src/readii/io/writers/nifti_writer.py (1 hunks)
  • tests/io/test_base_writer.py (1 hunks)
  • tests/io/test_nifti_writer.py (1 hunks)
  • tests/io/test_pattern_resolver.py (1 hunks)
🧰 Additional context used
🪛 Ruff (0.8.2)
tests/io/test_base_writer.py

2-2: os imported but unused

Remove unused import: os

(F401)


79-80: Use a single with statement with multiple contexts instead of nested with statements

(SIM117)


81-81: Local variable file_path is assigned to but never used

Remove assignment to unused variable file_path

(F841)

🔇 Additional comments (5)
src/readii/io/utils/__init__.py (1)

1-8: Module initialization is well-structured

The __init__.py file correctly initializes the module and specifies the public interface. The imports and __all__ variable are appropriately defined.

tests/io/test_nifti_writer.py (1)

6-21: LGTM! Well-structured test fixtures.

The fixtures provide a good foundation for testing, with appropriate sample data and default parameters.

src/readii/io/writers/base_writer.py (2)

100-101: Missing deletion of empty root directory in context manager exit.

The code logs that it's deleting the empty root directory but does not perform the deletion.


24-24: ⚠️ Potential issue

Fix pattern_resolver field declaration.

The pattern_resolver should be an instance variable, not a class variable, as it's initialized per instance in post_init.

-    pattern_resolver: PatternResolver = field(init=False)
+    pattern_resolver: PatternResolver = field(init=False, repr=False)

Likely invalid or redundant comment.

src/readii/io/writers/nifti_writer.py (1)

106-108: Verify SimpleITK compression level support.

The compressionLevel parameter might not be supported by sitk.WriteImage.

✅ Verification successful

Let me search for more specific information about SimpleITK's WriteImage function and its compression support.


Let me check the SimpleITK imports and any related test files to understand the context better.


Let me check the test files, particularly test_nifti_writer.py, to see how the compression is being tested.


Based on the evidence gathered, I can now provide the final response:

The compressionLevel parameter is correctly implemented and tested

The codebase shows proper implementation and testing of SimpleITK's compression level support:

  • The parameter is validated within bounds (0-9)
  • Comprehensive tests verify writing with different compression levels (0, 5, 9)
  • Tests pass successfully when writing compressed NIfTI files
  • The implementation follows SimpleITK's documented API
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for SimpleITK compression level documentation
rg -A 5 "compression.*level" 

Length of output: 4128


Script:

#!/bin/bash
# Search for SimpleITK WriteImage usage and documentation
rg -A 3 "sitk\.WriteImage" 

# Search for any SimpleITK compression related tests or documentation
rg -i "simpleitk.*compress"

Length of output: 599


Script:

#!/bin/bash
# Search for SimpleITK imports and related test files
rg -A 5 "import.*sitk"

# Look for test files containing sitk
fd "test.*\.py" --exec rg -l "sitk"

# Check if there are any test files specifically for image writing
fd "test.*\.py" --exec rg -l "WriteImage"

Length of output: 5181


Script:

#!/bin/bash
# Check the test file content for compression testing
cat tests/io/test_nifti_writer.py

# Look for any SimpleITK documentation files
fd -e md -e rst -e txt --exec rg -l "SimpleITK"

Length of output: 3384

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (2)
src/readii/io/utils/pattern_resolver.py (2)

11-15: Enhance the exception docstring.

The docstring could be more informative by including:

  • When this exception is raised
  • Example usage
  • List of possible error messages
 class PatternResolverError(Exception):
-    """Base exception for errors in pattern resolution."""
+    """Exception raised for errors in pattern resolution.
+
+    This exception is raised when:
+    - The filename format pattern is invalid
+    - Required keys are missing during pattern resolution
+    - Context values contain None
+
+    Examples
+    --------
+    >>> resolver = PatternResolver("{invalid")
+    Raises: PatternResolverError("Invalid filename format: Unmatched brackets")
+    """

52-70: Consider improving attribute declarations and logging.

The initialization logic is good, but could be enhanced for better maintainability and debugging.

 @dataclass
 class PatternResolver:
     filename_format: str = field(init=True)
+    pattern_parser: PatternParser = field(init=False)
+    formatted_pattern: str = field(init=False)
+    keys: list[str] = field(init=False)
     
     DEFAULT_PATTERN: ClassVar[re.Pattern] = re.compile(r"%(\w+)|\{(\w+)\}")
     
     def __init__(self, filename_format: str) -> None:
         self.filename_format = filename_format
 
         try:
             self.pattern_parser = PatternParser(
                 self.filename_format, pattern_parser=self.DEFAULT_PATTERN
             )
             self.formatted_pattern, self.keys = self.parse()
         except InvalidPatternError as e:
             msg = f"Invalid filename format: {e}"
             raise PatternResolverError(msg) from e
         else:
-            logger.debug("All keys are valid.", keys=self.keys)
-            logger.debug("Formatted Pattern valid.", formatted_pattern=self.formatted_pattern)
+            logger.debug(
+                "Pattern validation successful",
+                filename_format=self.filename_format,
+                keys=self.keys,
+                formatted_pattern=self.formatted_pattern
+            )
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cdfcdfc and 08a899a.

📒 Files selected for processing (1)
  • src/readii/io/utils/pattern_resolver.py (1 hunks)
🧰 Additional context used
📓 Learnings (1)
src/readii/io/utils/pattern_resolver.py (1)
Learnt from: jjjermiah
PR: bhklab/readii#84
File: src/readii/io/utils/pattern_resolver.py:5-6
Timestamp: 2024-12-13T16:20:45.016Z
Learning: Dependencies on `med-imagetools` in `readii` are acceptable, as the package is intended to work off of `med-imagetools`.
🔇 Additional comments (4)
src/readii/io/utils/pattern_resolver.py (4)

1-9: LGTM! Clean import organization.

Imports are well-organized, with standard library imports separated from external dependencies.


17-50: Excellent documentation!

The class docstring is comprehensive, with clear examples and explanations of the pattern matching behavior.


71-87: LGTM! Well-documented method.

The parse method is clean, well-documented, and serves its purpose effectively.


1-120: Verify pattern resolution functionality across the codebase.

Let's ensure the pattern resolution is used consistently and correctly across the codebase.

✅ Verification successful

Let me analyze the results and generate additional verification scripts to get a complete picture.


Let me analyze the test file to ensure proper test coverage of the pattern resolution functionality.


Based on the comprehensive analysis of the code and test files, I can now generate the final response:

Pattern resolution functionality is well-implemented and properly tested

The pattern resolution implementation is robust and correctly used across the codebase:

  • Comprehensive test suite covers various scenarios including error cases
  • Properly integrated in BaseWriter as a core component for path resolution
  • No instances of unsafe pattern formatting found that should be using PatternResolver
  • Clear documentation and consistent usage pattern
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check usage of PatternResolver across the codebase
# and verify pattern formats being used.

# Find all pattern formats being used
echo "Checking pattern formats in use:"
rg -U "PatternResolver\(['\"].*?['\"]\)" --multiline

# Check for potential string formatting that might need to use PatternResolver
echo -e "\nChecking for potential missed opportunities to use PatternResolver:"
rg "f['\"].*?\{.*?\}['\"]" --multiline

# Look for any direct string formatting that might need validation
echo -e "\nChecking for direct string formatting that might need validation:"
rg "%.+?[sd]" --type py

Length of output: 5355


Script:

#!/bin/bash
# Check for actual instantiations and usage of PatternResolver
echo "Checking for PatternResolver instantiations and usage:"
rg -A 5 "PatternResolver\(" --type py

# Check for imports of PatternResolver
echo -e "\nChecking for imports of PatternResolver:"
rg "from.*PatternResolver import|import.*PatternResolver" --type py

# Look for any test files related to PatternResolver
echo -e "\nChecking for test files:"
fd "test.*pattern.*resolver" --type f

Length of output: 1606


Script:

#!/bin/bash
# Check the content of the test file
echo "Checking test file content:"
cat tests/io/test_pattern_resolver.py

# Check BaseWriter implementation to see how PatternResolver is used
echo -e "\nChecking BaseWriter implementation:"
cat src/readii/io/writers/base_writer.py

Length of output: 5427

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (2)
tests/io/test_nifti_writer.py (1)

4-4: Remove unused import 'Path'

The imported Path from pathlib is not used in this file. Removing it will clean up the code.

Apply this diff to remove the unused import:

-from pathlib import Path
🧰 Tools
🪛 Ruff (0.8.2)

4-4: pathlib.Path imported but unused

Remove unused import: pathlib.Path

(F401)

src/readii/io/writers/nifti_writer.py (1)

115-117: Catch specific exceptions instead of 'Exception'

Catching the base Exception can obscure the source of errors and make debugging difficult. Consider catching specific exceptions that sitk.WriteImage or ImageFileWriter might raise, such as RuntimeError.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 08a899a and fb82114.

📒 Files selected for processing (3)
  • src/readii/io/utils/pattern_resolver.py (1 hunks)
  • src/readii/io/writers/nifti_writer.py (1 hunks)
  • tests/io/test_nifti_writer.py (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/readii/io/utils/pattern_resolver.py
🧰 Additional context used
🪛 Ruff (0.8.2)
tests/io/test_nifti_writer.py

4-4: pathlib.Path imported but unused

Remove unused import: pathlib.Path

(F401)

🔇 Additional comments (2)
src/readii/io/writers/nifti_writer.py (2)

91-98: Support for numpy array input implemented correctly

The addition of support for NumPy arrays in the save method enhances the versatility of the NIFTIWriter. This allows users to save images without explicitly converting arrays to SimpleITK images beforehand.


112-114: ⚠️ Potential issue

Invalid use of compressionLevel parameter in sitk.WriteImage

The sitk.WriteImage function does not accept a compressionLevel parameter. Including it will result in a TypeError. To set the compression level, you should use sitk.ImageFileWriter instead.

Apply this change to use ImageFileWriter with compression level:

writer = sitk.ImageFileWriter()
writer.SetFileName(str(out_path))
writer.UseCompressionOn()
writer.SetCompressionLevel(self.compression_level)
writer.Execute(image)

Replace lines 112-114 with the code above.

@jjjermiah jjjermiah merged commit 7228770 into main Dec 13, 2024
17 checks passed
@jjjermiah jjjermiah deleted the jjjermiah/feature/nifti-writer branch December 18, 2024 18:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

2 participants