diff --git a/pyproject.toml b/pyproject.toml index 5b91df9..cab7973 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -139,7 +139,9 @@ extend-select = [ ignore = [ "PLR09", # Too many <...> "PLR2004", # Magic value used in comparison - "ISC001", # Conflicts with formatter + "ISC001", # Conflicts with formatter, + "EM101", # Exception must not use a string literal, assign to variable first + "EM102", # Exception must not use an f-string literal, assign to variable first ] isort.required-imports = ["from __future__ import annotations"] # Uncomment if using a _compat.typing backport @@ -162,4 +164,5 @@ messages_control.disable = [ "missing-module-docstring", "missing-function-docstring", "wrong-import-position", + "C0103", # Attribute name doesn't conform to snake_case naming style ] diff --git a/src/pydcmqi/segimage.py b/src/pydcmqi/segimage.py index 4e81f2b..5dc3d83 100644 --- a/src/pydcmqi/segimage.py +++ b/src/pydcmqi/segimage.py @@ -17,9 +17,9 @@ def get_min_max_values(image: sitk.Image) -> tuple[float, float]: - filter = sitk.MinimumMaximumImageFilter() - filter.Execute(image) - return filter.GetMinimum(), filter.GetMaximum() + sitk_filter = sitk.MinimumMaximumImageFilter() + sitk_filter.Execute(image) + return sitk_filter.GetMinimum(), sitk_filter.GetMaximum() def _path(path: str | Path) -> Path: @@ -122,7 +122,24 @@ class SegmentData: """ def __init__(self) -> None: - self._data: SegmentDict = {} + self._data: SegmentDict = { + "labelID": 0, + "SegmentLabel": "", + "SegmentDescription": "", + "SegmentAlgorithmName": "", + "SegmentAlgorithmType": "", + "recommendedDisplayRGBValue": [0, 0, 0], + "SegmentedPropertyCategoryCodeSequence": { + "CodeMeaning": "", + "CodeValue": "", + "CodingSchemeDesignator": "", + }, + "SegmentedPropertyTypeCodeSequence": { + "CodeMeaning": "", + "CodeValue": "", + "CodingSchemeDesignator": "", + }, + } def setConfigData(self, config: dict) -> None: self._data = config.copy() @@ -319,6 +336,10 @@ def hasAnatomicRegionModifier(self) -> bool: class Segment: + """ + A class to store and manipulate the data for a segmentation or region of interest. + """ + def __init__(self) -> None: self.path: Path | None = None self.data = SegmentData() @@ -426,6 +447,10 @@ def saveAsBinary(self, path: str | Path) -> None: class SegImageData: + """ + A class to store and manipulate the data for a segmentation or region of interest. + """ + def __init__(self) -> None: self._data: SegImageDict = {} @@ -511,6 +536,10 @@ def asdict(self) -> SegImageDict: class SegImageFiles: + """ + A class to store and manipulate the file paths for a segmentation or region of interest. + """ + def __init__(self) -> None: self._dicomseg: Path | None = None self._config: Path | None = None @@ -525,6 +554,10 @@ def config(self) -> Path | None: class SegImage: + """ + A class to store and manipulate the data for a segmentation or region of interest. + """ + verbose: bool = False @classmethod @@ -563,7 +596,7 @@ def load( dicomseg_file: Path | str, output_dir: Path | str | None = None, ) -> bool: - print(f"Converting file: {dicomseg_file} into {output_dir}.") + # print(f"Converting file: {dicomseg_file} into {output_dir}.") # TODO: use logging # we create a temporary output directory if none is provided in the specified tmp dir if output_dir is None: @@ -594,7 +627,7 @@ def load( self._import(output_dir) # update file paths - self.files._dicomseg = dicomseg_file + self.files._dicomseg = dicomseg_file # pylint: disable=W0212 def _import( self, output_dir: Path, disable_file_sanity_checks: bool = False @@ -610,7 +643,7 @@ def _import( config_file = output_dir / "pydcmqi-meta.json" # load the config file - with Path.open(config_file) as f: + with Path.open(config_file, encoding="utf-8") as f: self._config = json.load(f) # load data @@ -635,7 +668,7 @@ def _import( self.loaded = True # store file paths - self.files._config = config_file + self.files._config = config_file # pylint: disable=W0212 def write( self, @@ -678,11 +711,11 @@ def write( # store in the output directory # but for now just print - print(json.dumps(config, indent=2)) + # print(json.dumps(config, indent=2)) # TODO: use logging # store in _debug_test_meta.json meta_tmp_file = Path(self.tmp_dir) / "_debug_test_meta.json" - with Path.open(meta_tmp_file, "w") as f: + with Path.open(meta_tmp_file, "w", encoding="utf-8") as f: json.dump(config, f, indent=2) # export config file if requested diff --git a/src/pydcmqi/types.py b/src/pydcmqi/types.py index da1dedf..aaace3b 100644 --- a/src/pydcmqi/types.py +++ b/src/pydcmqi/types.py @@ -4,12 +4,20 @@ class TripletDict(TypedDict): + """ + A dictionary defining the keys for any generic triplet. + """ + CodeMeaning: str CodeValue: str CodingSchemeDesignator: str class SegmentDict(TypedDict): + """ + A dictionary defining the keys for a single segment within a segmentation image. + """ + labelID: int SegmentLabel: str SegmentDescription: str @@ -21,6 +29,10 @@ class SegmentDict(TypedDict): class SegImageDict(TypedDict): + """ + A dictionary defining the keys for a segmentation image. + """ + BodyPartExamined: str ClinicalTrialCoordinatingCenterName: str ClinicalTrialSeriesID: str diff --git a/tests/test_segimage.py b/tests/test_segimage.py index 301e7ae..6694b57 100644 --- a/tests/test_segimage.py +++ b/tests/test_segimage.py @@ -168,7 +168,10 @@ def test_tmp_dir(self): assert isinstance(segimg4.tmp_dir, Path) # specifying a tmp_dir with e.g. a numeric type will raise an ValueError - with pytest.raises(ValueError): + with pytest.raises( + ValueError, + match="Invalid tmp_dir, must be either None for default, a Path or a string.", + ): _ = SegImage(tmp_dir=1)