Skip to content

Commit

Permalink
fix: Update to pydicom 3.0 and fixed some typos
Browse files Browse the repository at this point in the history
  • Loading branch information
alanwilter committed Sep 9, 2024
1 parent 3383f1a commit 0cdfddb
Show file tree
Hide file tree
Showing 13 changed files with 1,237 additions and 1,133 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,15 @@ jobs:

- name: Create Release
if: steps.check.outputs.bump == 'yes'
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.bump.outputs.version }}
release_name: Release ${{ steps.bump.outputs.version }}
name: Release ${{ steps.bump.outputs.version }}
body: ${{ steps.changelog.outputs.changelog }}
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Publish to PyPI
if: steps.check.outputs.bump == 'yes'
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,5 @@ cython_debug/
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

example_dir/
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ repos:
- id: check-merge-conflict
- id: check-docstring-first
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.3
rev: v0.6.4
hooks:
- id: ruff
args: ["--fix"]
files: ^hooks
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.3
rev: v0.6.4
hooks:
- id: ruff-format
files: ^hooks
Expand Down
2 changes: 2 additions & 0 deletions .talismanrc
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ fileignoreconfig:
checksum: fdb35056dbd109dbbac6fd5d3668fbff90ace8f46aa37b787eaa2572c02d0aaf
- filename: README.md
checksum: 4a9bf4e1400f36c5646be2b85eee66d1681d12e67bd9dce431206d21a7c85f64
- filename: poetry.lock
checksum: 868367c1e6faa49cc06eaf099af0f27c7bc2ad9eab2be84b26ae7b41a953bcf7
version: ""
2,028 changes: 1,051 additions & 977 deletions poetry.lock

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions process_dcm/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1 @@
"""process_dcm module."""

__version__ = "0.2.0"
3 changes: 3 additions & 0 deletions process_dcm/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from process_dcm.main import app

app(prog_name="process-dcm")
21 changes: 10 additions & 11 deletions process_dcm/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
def print_version(value: bool) -> None:
"""Print the version of the app."""
if value:
typer.echo(f"Process DCM Version: {__version__}")
typer.secho(f"Process DCM Version: {__version__}", fg="blue")
raise typer.Exit()


Expand Down Expand Up @@ -82,18 +82,17 @@ def main(
len_sf, base_dir, subfolders = find_dicom_folders_with_base(input_dir)
output_dirs = []

if relative and os.path.isabs(output_dir):
relative = False
typer.secho(
"WARN: '--relative' x 'absolute --output_dir' are incompatible, absolute 'output_dir' takes precedence",
fg="yellow",
)
output_dirs = [x.replace(base_dir, output_dir) for x in subfolders]
elif relative and not os.path.isabs(output_dir):
output_dirs = [os.path.join(x, output_dir) for x in subfolders]
elif not relative and not os.path.isabs(output_dir):
if os.path.isabs(output_dir):
if relative:
typer.secho(
"WARN: '--relative' x 'absolute --output_dir' are incompatible, absolute 'output_dir' takes precedence",
fg="yellow",
)
relative = False
output_dir = os.path.abspath(output_dir)
output_dirs = [x.replace(base_dir, output_dir) for x in subfolders]
elif relative:
output_dirs = [os.path.join(x, output_dir) for x in subfolders]

tasks = list(zip(subfolders, output_dirs))

Expand Down
3 changes: 0 additions & 3 deletions process_dcm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,6 @@ def meta_images(dcm_obj: FileDataset) -> dict:
return meta


boo = "hello"


def process_dcm_meta(
dcm_objs: list[FileDataset], output_dir: str, mapping: str = "", keep: str = ""
) -> tuple[str, str]:
Expand Down
54 changes: 31 additions & 23 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,9 @@ classifiers = [
[tool.poetry.dependencies]
python = "^3.11"
opencv-python-headless = "^4.10.0.84"
typer = "^0.12.3"
tqdm = "^4.66.4"
pydicom = "^2.4.4"
typer = "^0.12.5"
tqdm = "^4.66.5"
pydicom = "^3.0.0"
natsort = "^8.4.0"
pillow = "^10.4.0"
python-gdcm = "^3.0.24.1"
Expand All @@ -31,32 +31,37 @@ python-gdcm = "^3.0.24.1"
process-dcm = "process_dcm.main:cli"

[tool.poetry.group.dev.dependencies]
boto3-stubs = { extras = ["lambda", "s3"], version = "*" }
ipdb = "*"
ipython = "*"
jupyterlab = "*"
mypy = "*"
mypy-boto3-lambda = "*"
mypy-boto3-s3 = "*"
pandas-stubs = "*"
pdbpp = "*"
pip = "*"
pre-commit = "*"
pytest-cov = "*"
pytest-env = "*"
pytest-xdist = "*"
ruff = "*"
typed-argument-parser = "*"
types-pillow = "*"
types-tqdm = "*"
wheel = "*"
boto3-stubs = { extras = ["lambda", "s3"], version = "1.35.14" }
ipdb = "0.13.13"
ipython = "8.27.0"
jupyterlab = "4.2.5"
mypy = "1.11.2"
mypy-boto3-lambda = "1.35.3"
mypy-boto3-s3 = "1.35.2"
pandas-stubs = "2.2.2.240807"
pdbpp = "0.10.3"
pip = "24.2"
pre-commit = "3.8.0"
pytest-cov = "5.0.0"
pytest-env = "1.1.4"
pytest-xdist = "3.6.1"
ruff = "0.6.4"
typed-argument-parser = "1.10.1"
types-pillow = "10.2.0.20240822"
types-tqdm = "4.66.0.20240417"
wheel = "0.44.0"
pytest-mock = "^3.14.0"

[tool.pytest.ini_options]
addopts = "tests --cov=process_dcm/ --cov-report=term-missing:skip-covered --cov-report=xml --dist=loadgroup -n 8 --durations=5"

[tool.coverage.report]
exclude_lines = ["no cov", "if __name__ == .__main__.:", "if TYPE_CHECKING:"]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"app()",
]

[tool.ruff]
target-version = "py311"
Expand All @@ -74,6 +79,9 @@ convention = "google"

[tool.ruff.lint.per-file-ignores]
"tests/*" = ["D100", "D103", "D104"]
"__main__.py" = ["D100"]
"__init__.py" = ["D104"]


[tool.mypy]
python_version = "3.11"
Expand Down
77 changes: 71 additions & 6 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import datetime
import os
import tempfile
from collections import defaultdict
from collections.abc import Generator
from pathlib import Path

import pydicom
import pytest
from pydicom.dataset import Dataset, FileDataset
from pydicom.dataset import Dataset, FileDataset, FileMetaDataset
from typer.testing import CliRunner

from process_dcm import __version__ as version
Expand Down Expand Up @@ -35,7 +36,19 @@ def input_dir():
@pytest.fixture
def dicom_opotopol():
"""Fixture to create a mocked DICOM FileDataset for OPTOPOL."""
dataset = FileDataset("tests/example-dcms/bscans.dcm", {}, file_meta=defaultdict(str), preamble=b"\0" * 128)
# Create a FileMetaDataset instead of a defaultdict
file_meta = FileMetaDataset()
file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian # or other appropriate UID

# Create the FileDataset object
dataset = FileDataset(
"tests/example-dcms/bscans.dcm",
{},
file_meta=file_meta,
preamble=b"\0" * 128,
is_implicit_VR=False,
is_little_endian=True,
)

# Mock setting relevant fields to simulate OPTOPOL DICOM
dataset.Modality = "OPT"
Expand All @@ -51,13 +64,30 @@ def dicom_opotopol():

dataset.PerFrameFunctionalGroupsSequence = [functional_group]

# Optionally set the current date and time for the dataset
dt = datetime.datetime.now()
dataset.ContentDate = dt.strftime("%Y%m%d")
dataset.ContentTime = dt.strftime("%H%M%S.%f") # fractional seconds

return dataset


@pytest.fixture
def dicom_attribute_error():
"""Fixture to create a mocked DICOM FileDataset for AttributeError simulation."""
dataset = FileDataset("tests/example-dcms/bscans.dcm", {}, file_meta=defaultdict(str), preamble=b"\0" * 128)
# Create a FileMetaDataset instead of a defaultdict
file_meta = FileMetaDataset()
file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian # or appropriate UID

# Create the FileDataset object
dataset = FileDataset(
"tests/example-dcms/bscans.dcm",
{},
file_meta=file_meta,
preamble=b"\0" * 128,
is_implicit_VR=False,
is_little_endian=True,
)

# Mock setting relevant fields to simulate a DICOM file
dataset.Modality = "OPT"
Expand All @@ -67,18 +97,36 @@ def dicom_attribute_error():
dataset.NumberOfFrames = 5
dataset.AccessionNumber = 0

# Add the PerFrameFunctionalGroupsSequence
functional_group = Dataset()
functional_group.OphthalmicFrameLocationSequence = []

dataset.PerFrameFunctionalGroupsSequence = [functional_group]

# Optionally set the current date and time for the dataset
dt = datetime.datetime.now()
dataset.ContentDate = dt.strftime("%Y%m%d")
dataset.ContentTime = dt.strftime("%H%M%S.%f") # fractional seconds

return dataset


@pytest.fixture
def dicom_with_photo_locations():
"""Fixture to create a mocked DICOM FileDataset for valid photo locations."""
dataset = FileDataset("tests/example-dcms/bscans.dcm", {}, file_meta=defaultdict(str), preamble=b"\0" * 128)
# Create a FileMetaDataset instead of a defaultdict
file_meta = FileMetaDataset()
file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian # or appropriate UID

# Create the FileDataset object
dataset = FileDataset(
"tests/example-dcms/bscans.dcm",
{},
file_meta=file_meta,
preamble=b"\0" * 128,
is_implicit_VR=False,
is_little_endian=True,
)

# Mock setting relevant fields to simulate a DICOM file
dataset.Modality = "OPT"
Expand All @@ -92,21 +140,38 @@ def dicom_with_photo_locations():
frame_location = Dataset()
frame_location.OphthalmicFrameLocationSequence = [Dataset()]
frame_location.OphthalmicFrameLocationSequence[0].ReferenceCoordinates = [0.0, 0.0, 512.0, 512.0]

# Repeat the same PerFrameFunctionalGroupsSequence for each frame
dataset.PerFrameFunctionalGroupsSequence = [frame_location for _ in range(dataset.NumberOfFrames)]

# Optionally set the current date and time for the dataset
dt = datetime.datetime.now()
dataset.ContentDate = dt.strftime("%Y%m%d")
dataset.ContentTime = dt.strftime("%H%M%S.%f") # fractional seconds

return dataset


@pytest.fixture
def dicom_base():
"""Base fixture for creating a mocked DICOM FileDataset."""
dataset = FileDataset("test.dcm", {}, file_meta=defaultdict(str), preamble=b"\0" * 128)
# Create a FileMetaDataset instead of a defaultdict
file_meta = FileMetaDataset()
file_meta.TransferSyntaxUID = pydicom.uid.ExplicitVRLittleEndian # or appropriate UID

# Create the FileDataset object
dataset = FileDataset(
"test.dcm", {}, file_meta=file_meta, preamble=b"\0" * 128, is_implicit_VR=False, is_little_endian=True
)

# Mock setting relevant fields to simulate a basic DICOM file
dataset.AccessionNumber = 0
dataset.Modality = ImageModality.OCT
dataset.PatientBirthDate = "19020202"
dataset.Manufacturer = ""
dataset.SeriesDescription = ""
dataset.PatientID = "bbff7a25-d32c-4192-9330-0bb01d49f746"

return dataset


Expand Down
Loading

0 comments on commit 0cdfddb

Please sign in to comment.