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

Fix py dcm version #2

Merged
merged 5 commits into from
Sep 30, 2024
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
38 changes: 28 additions & 10 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,17 +1,39 @@
name: Test and Release
name: Test and Release Workflow

on:
pull_request:
branches:
- "*" # Run tests on all branches for PRs
push:
branches:
- main
- main # Run tests and potentially release on pushes to main
paths:
- "**/*.py"
- pyproject.toml
- poetry.lock

jobs:
test-and-release:
name: Test and Release
run-tests:
name: Run Tests
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"
- name: Install dependencies
run: |
pip install poetry
poetry install
- name: Run tests
run: poetry run pytest -k 'not test_main_mapping_example_dir_relative and not test_main_mapping_example_dir and not test_process_dcm'

release:
name: Release
needs: run-tests
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
permissions:
contents: write
Expand All @@ -20,20 +42,14 @@ jobs:
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Install dependencies
run: |
pip install poetry commitizen
poetry install

- name: Run tests
run: poetry run pytest -k 'not test_main_mapping_example_dir_relative and not test_main_mapping_example_dir and not test_process_dcm'

- name: Check if version bump is needed
id: check
continue-on-error: true
Expand All @@ -42,6 +58,7 @@ jobs:
echo "bump=$(cz bump --dry-run 2>&1 | grep -q 'bump:' && echo 'yes' || echo 'no')" >> $GITHUB_OUTPUT

- name: Set up Git user
if: steps.check.outputs.bump == 'yes'
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
Expand All @@ -54,6 +71,7 @@ jobs:
echo "version=$(git describe --tags --abbrev=0)" >> $GITHUB_OUTPUT

- name: Build project
if: steps.check.outputs.bump == 'yes'
run: poetry build

- name: Generate Changelog
Expand Down
6 changes: 3 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,18 @@ repos:
- id: check-merge-conflict
- id: check-docstring-first
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
rev: v0.6.8
hooks:
- id: ruff
args: ["--fix"]
files: ^hooks
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.6.5
rev: v0.6.8
hooks:
- id: ruff-format
files: ^hooks
- repo: https://github.com/commitizen-tools/commitizen
rev: v3.29.0
rev: v3.29.1
hooks:
- id: commitizen
stages:
Expand Down
8 changes: 1 addition & 7 deletions .talismanrc
Original file line number Diff line number Diff line change
@@ -1,10 +1,4 @@
fileignoreconfig:
- filename: poetry.lock
checksum: 1c2b16ed82e9ee932d71256bd8951b7bb6615a0bed7b0aae452ba97e5fcbe769
- filename: tests/test_utils.py
checksum: a674113efa3a5e2dba900599efe3bdbe16ce88dca297ec57752ad20afb399908
- filename: tests/test_main.py
checksum: 59a23ed618d9f4f2b0e357444be748cd00233bcf7428a8afde1b3be89258a103
- filename: process_dcm/utils.py
checksum: c7e686f07cf036a427a8dcb3719affc72d5de9357b48625c688aab01614f57f3
checksum: 0ad40dc799769c4734b1f5791c32eb5657f6a337fd70492d16b4b8878bdcbf9f
version: ""
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,5 +39,6 @@
"source.organizeImports": "explicit"
},
"editor.formatOnType": true
}
},
"conventionalCommits.scopes": ["deps"]
}
199 changes: 100 additions & 99 deletions poetry.lock

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions process_dcm/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def main(
fg=typer.colors.BRIGHT_YELLOW,
)

len_sf, base_dir, subfolders = find_dicom_folders_with_base(input_dir)
len_sf, base_dir, subfolders = find_dicom_folders_with_base(os.path.abspath(input_dir))
output_dirs = []

if os.path.isabs(output_dir):
Expand All @@ -121,7 +121,7 @@ def main(
output_dirs = [os.path.join(x, output_dir) for x in subfolders]
else:
output_dir = os.path.abspath(output_dir)
output_dirs = [x.replace(base_dir, output_dir) for x in subfolders]
output_dirs = [x.replace(os.path.abspath(base_dir), output_dir) for x in subfolders]

tasks = list(zip(subfolders, output_dirs))

Expand Down
25 changes: 15 additions & 10 deletions process_dcm/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
from pydicom.dataset import FileDataset
from pydicom.filereader import dcmread

from process_dcm import __version__
from process_dcm.const import RESERVED_CSV, ImageModality

warnings.filterwarnings("ignore", category=UserWarning, message="A value of type *")
Expand Down Expand Up @@ -153,7 +154,7 @@ def process_dcm_meta(
metadata["series"] = {}
metadata["images"]["images"] = []
metadata["parser_version"] = [1, 5, 2]
metadata["py_dcm_version"] = [0, 1, 0]
metadata["py_dcm_version"] = list(map(int, __version__.split(".")))

keep_gender = "g" in keep
keep_names = "n" in keep
Expand Down Expand Up @@ -449,18 +450,22 @@ def find_dicom_folders_with_base(root_folder: str) -> tuple[int, str, list[str]]
return len_ins, base_dir, natsorted(folders)


def get_md5(file_path: Path | str | list[str]) -> str:
"""Calculate the MD5 checksum of a file or list of files."""
def get_md5(file_path: Path | str | list[str], minus: int = 0) -> str:
"""Calculate the MD5 checksum of a file or list of files, optionally suppressing lines from the bottom."""
md5_hash = hashlib.md5()
if isinstance(file_path, str) or isinstance(file_path, Path):
with open(file_path, "rb") as f:
while chunk := f.read(4096):
md5_hash.update(chunk)

def process_file(file: Path | str) -> None:
with open(file, "rb") as f:
lines = f.readlines()
for line in lines[:-minus] if minus > 0 else lines:
md5_hash.update(line)

if isinstance(file_path, str | Path):
process_file(file_path)
elif isinstance(file_path, list):
for fname in file_path:
with open(fname, "rb") as f:
while chunk := f.read(4096):
md5_hash.update(chunk)
process_file(fname)

return md5_hash.hexdigest()


Expand Down
12 changes: 6 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ classifiers = [
python = "^3.10"
opencv-python-headless = "^4.10.0.84"
typer = "^0.12.5"
pydicom = "^3.0.0"
pydicom = "^3.0.1"
natsort = "^8.4.0"
pillow = "^10.4.0"
python-gdcm = "^3.0.24.1"
Expand All @@ -30,21 +30,21 @@ python-gdcm = "^3.0.24.1"
process-dcm = "process_dcm.main:cli"

[tool.poetry.group.dev.dependencies]
boto3-stubs = { extras = ["lambda", "s3"], version = "1.35.19" }
boto3-stubs = { extras = ["lambda", "s3"], version = "1.35.29" }
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.16"
mypy-boto3-lambda = "1.35.28"
mypy-boto3-s3 = "1.35.22"
pandas-stubs = "2.2.2.240909"
pdbpp = "0.10.3"
pip = "24.2"
pre-commit = "3.8.0"
pytest-cov = "5.0.0"
pytest-env = "1.1.4"
pytest-env = "1.1.5"
pytest-xdist = "3.6.1"
ruff = "0.6.5"
ruff = "0.6.8"
typed-argument-parser = "1.10.1"
wheel = "0.44.0"
pytest-mock = "^3.14.0"
Expand Down
5 changes: 4 additions & 1 deletion tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
from process_dcm import __version__ as version
from process_dcm.const import ImageModality

bottom = 11


def pytest_report_header():
return f">>>\tVersion: {version}\n"
Expand Down Expand Up @@ -46,7 +48,8 @@ def del_file_paths(file_paths: list[str]) -> None:
os.rmdir(path)


def create_directory_structure(base_path, structure):
def create_directory_structure(base_path: Path, structure: dict) -> None:
"""Creates a directory structure recursively."""
for item, content in structure.items():
path = base_path / item
if isinstance(content, dict):
Expand Down
41 changes: 22 additions & 19 deletions tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from process_dcm.const import RESERVED_CSV
from process_dcm.main import app, process_task
from process_dcm.utils import get_md5
from tests.conftest import remove_ansi_codes
from tests.conftest import bottom, remove_ansi_codes


def test_main_defaults(runner):
Expand Down Expand Up @@ -59,9 +59,9 @@ def test_cli_without_args(runner):
@pytest.mark.parametrize(
"md5, meta, keep",
[
(["837808d746aef8e2dd08defbdbc70818"], "0a9a930806f2784aa4e60d47b3bad6ed", "pndg"),
(["7a355bb7e0c95155d1541c7fe0941c5e"], "fd6c5a84aca6499b0ea8b99d4e25dc92", "pnDg"),
(["2319181ecfc33d35b01dcec65ab2c568"], "35fe295648681e3521da8dddaed63705", ""),
(["5ba37cc43233db423394cf98c81d5fbc"], "27e4fa04ad730718b2509af36743c995", "pndg"),
(["5ba37cc43233db423394cf98c81d5fbc"], "3a15fdd18a67b3d4ce7be6164f58f073", "pnDg"),
(["5ba37cc43233db423394cf98c81d5fbc"], "ba5973bc8dd8e15aa6bef95bcd248fbf", ""),
],
)
def test_main(md5, meta, keep, janitor, runner):
Expand All @@ -83,9 +83,10 @@ def test_main(md5, meta, keep, janitor, runner):
]
result = runner.invoke(app, args)
assert result.exit_code == 0
of = sorted(glob(f"{output_dir}/**/*"))
assert len(of) == 51
assert get_md5(output_dir / "example-dcms/metadata.json") == meta
tof = sorted(glob(f"{output_dir}/**/*"))
of = [x for x in tof if "metadata.json" not in x]
assert len(tof) == 51
assert get_md5(output_dir / "example-dcms/metadata.json", bottom) == meta
assert get_md5(of) in md5


Expand All @@ -94,10 +95,11 @@ def test_main_dummy(janitor, runner):
args = ["tests/dummy_ex", "-o", "dummy_dir", "-k", "p"]
result = runner.invoke(app, args)
assert result.exit_code == 0
of = sorted(glob("dummy_dir/**/*"))
assert len(of) == 2
assert get_md5("dummy_dir/dummy_ex/metadata.json") == "1cabdb14492a0e62d80cfc0a3fe304e9"
assert get_md5(of) in "b19bbfca59584915295f67e9259880d7"
tof = sorted(glob("dummy_dir/**/*"))
of = [x for x in tof if "metadata.json" not in x]
assert len(tof) == 2
assert get_md5("dummy_dir/dummy_ex/metadata.json", bottom) == "0fbf0de5556e77447925ff3bfaf8168e"
assert get_md5(of) in ["377b5a17c284226518ccef9bddff25af"]


def test_main_mapping(janitor, runner):
Expand All @@ -121,10 +123,11 @@ def test_main_mapping(janitor, runner):
result = runner.invoke(app, args)
assert result.exit_code == 0
assert "WARN: '--relative' x 'absolute --output_dir'" in result.output
of = sorted(glob(f"{output_dir}/**/*"))
assert len(of) == 51
assert get_md5(output_dir / "example-dcms/metadata.json") == "261826ad2e067e9adb7143bb6c053dbc"
assert get_md5(of) in "6ff8e2fe69c5fbe86f81f44f74496cab"
tof = sorted(glob(f"{output_dir}/**/*"))
of = [x for x in tof if "metadata.json" not in x]
assert len(tof) == 51
assert get_md5(output_dir / "example-dcms/metadata.json", bottom) == "450e2e40d321a24219c1c9ec15b2c80e"
assert get_md5(of) in ["5ba37cc43233db423394cf98c81d5fbc"]


def test_main_abort(runner):
Expand All @@ -151,8 +154,8 @@ def test_main_mapping_example_dir(janitor, runner):
assert result.exit_code == 0
of = sorted(glob(f"{output_dir}/**/**/*"))
assert len(of) == 262
assert get_md5(output_dir / "012345/20180724_L/metadata.json") == "1b46961177c80daf69e7dea7379fcc31"
assert get_md5(output_dir / "3517807670/20180926_R/metadata.json") == "bbf5c47f9fb28f46b4cc1bf08c311593"
assert get_md5(output_dir / "012345/20180724_L/metadata.json", bottom) == "93fff12758d6c0f9098e7fd5e8c8304e"
assert get_md5(output_dir / "3517807670/20180926_R/metadata.json", bottom) == "b9ff35a765db6b1eaeac4253c93a6044"


# skip this test for CI
Expand All @@ -170,8 +173,8 @@ def test_main_mapping_example_dir_relative(janitor, runner):
janitor.append(path1)
janitor.append(path2)
assert len(of) == 262
assert get_md5(path1 / "20180724_L/dummy/metadata.json") == "1b46961177c80daf69e7dea7379fcc31"
assert get_md5(path2 / "20180926_R/dummy/metadata.json") == "bbf5c47f9fb28f46b4cc1bf08c311593"
assert get_md5(path1 / "20180724_L/dummy/metadata.json", bottom) == "93fff12758d6c0f9098e7fd5e8c8304e"
assert get_md5(path2 / "20180926_R/dummy/metadata.json", bottom) == "b9ff35a765db6b1eaeac4253c93a6044"


def test_process_task():
Expand Down
Loading