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
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,24 @@
from dicom_validator.spec_reader.edition_reader import EditionReader


def get_revision(revision, path):
def get_edition(edition, path):
reader = EditionReader(path)
# we want to recreate the json files for each test run,
# so we don't need them cached
reader.get_revision(revision, create_json=False)
reader.get_edition_path(edition, create_json=False)


if __name__ == "__main__":
parser = argparse.ArgumentParser(
description="Downloads a revision of the DICOM standard"
description="Downloads an edition of the DICOM standard"
)
parser.add_argument(
"revision",
help="Standard revision",
"edition",
help="Standard edition",
)
parser.add_argument(
"path",
help="Path for the DICOM specs",
)
args = parser.parse_args()
get_revision(args.revision, args.path)
get_edition(args.edition, args.path)
4 changes: 2 additions & 2 deletions .github/workflows/testsuite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ jobs:
if: steps.cache-dicom.outputs.cache-hit != 'true'
run: |
pip install -e .
python .github/workflows/get_revision.py 2015b "`pwd`/dicom_validator/tests/fixtures/standard"
python .github/workflows/get_revision.py 2025d "`pwd`/dicom_validator/tests/fixtures/standard"
python .github/workflows/get_edition.py 2015b "`pwd`/dicom_validator/tests/fixtures/standard"
python .github/workflows/get_edition.py 2025d "`pwd`/dicom_validator/tests/fixtures/standard"

- name: Install dependencies
run: |
Expand Down
13 changes: 11 additions & 2 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,27 @@ In line with pydicom, support for Python 3.9 will be removed in that version.
* added typing to production code
* the result dictionary generated by `IODValidator.validate()` has changed to an instance
of `ValidationResult`, and the log messages in the result have been replaced by error codes
* changed `--revision` to `--edition` to conform to DICOM vocabulary, deprecated
`--revision`; also renamed `revision` to `edition` elsewhere in function names
* logging the errors has been decoupled from validation via an error handler argument

### Fixes
* tags not allowed in multi-frame functional groups have been listed
as errors twice (see [#196](../../issues/196))

### Infrastructure
* added Python 3.14 to CI (currently needs development version of `pydicom`)
* removed support for Python 3.9

## [Version 0.7.3](https://pypi.python.org/pypi/dicom-validator/0.7.3) (2025-10-13)
Fixes handling of nested sequences.

### Fixes
* fixes to correctly evaluate SR documents (see [#206](../../issues/206)):
* sequences defined recursively in the standard are now supported
* conditions for including macros inside a sequence are now evaluated on the correct level

### Infrastructure
* added Python 3.14 to CI (currently needs development version of `pydicom`)
* removed support for Python 3.9
* updated the tests for current DICOM version 2025d

## [Version 0.7.2](https://pypi.python.org/pypi/dicom-validator/0.7.2) (2025-08-16)
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ pip install dicom-validator
## Usage
```
validate_iods [-h] [--standard-path STANDARD_PATH]
[--revision REVISION] [--force-read] [--recreate-json]
[--edition EDITION] [--force-read] [--recreate-json]
[--suppress-vr-warnings] [--verbose]
dicomfiles [dicomfiles ...]

dump_dcm_info [-h] [--standard-path STANDARD_PATH]
[--revision REVISION] [--max-value-len MAX_VALUE_LEN]
[--edition EDITION] [--max-value-len MAX_VALUE_LEN]
[--show-tags [SHOW_TAGS [SHOW_TAGS ...]]]
[--show-image-data] [--recreate-json]
dicomfiles [dicomfiles ...]
Expand All @@ -64,8 +64,8 @@ These files are then used by the tools. Periodically (once a month), the tools
check for a newer version of the DICOM standard and download it if found.

It is also possible to use older versions of the standard via the command line
option `--revision` or `-r`, provided they are available for download
(at the time of writing, standards are available since revision 2014a). A
option `--edition` or `-e`, provided they are available for download
(at the time of writing, standards are available since edition 2014a). A
list of currently available editions can be found in
*<user home>/dicom-validator/editions.json* after a tool has been called
the first time.
Expand All @@ -90,7 +90,7 @@ The output for a single file may look like this:
```
(py3_test) c:\dev\GitHub\dicom-validator>validate_iods "c:\dev\DICOM Data\WG02\Enhanced-XA\ENHXA"

Using DICOM revision 2023c
Using DICOM edition 2023c
SOP class is "1.2.840.10008.5.1.4.1.1.12.1.1" (Enhanced XA Image IOD)

Errors
Expand Down Expand Up @@ -139,7 +139,7 @@ Only the given standard is used to evaluate the files. If
the DICOM file has been written using an older standard, it may conform to
that standard, but not to the newest one. Tags that are retired in the
version of the standard used for parsing are not considered at all.
You can always check against an older standard by using the `--revision` option.
You can always check against an older standard by using the `--edition` option.

#### Enumerated values and defined terms
Most enumerated values are checked against, but some are ignored due to parsing issues.
Expand Down
53 changes: 53 additions & 0 deletions dicom_validator/command_line_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import argparse
import os
import warnings
from pathlib import Path

from dicom_validator.spec_reader.edition_reader import EditionReader
from dicom_validator.validator.dicom_info import DicomInfo


def add_edition_args(parser: argparse.ArgumentParser) -> None:
"""Add edition related arguments to argument parser."""
parser.add_argument(
"--standard-path",
"-src",
help="Base path with the DICOM specs in docbook and json format",
default=str(Path.home() / "dicom-validator"),
)
parser.add_argument(
"--edition",
"-e",
help='Standard edition (e.g. "2014c"), year of '
'edition, "current" or "local" (latest '
"locally installed)",
default="current",
)
parser.add_argument(
"--revision",
"-r",
help="Standard edition - deprecated, use --edition instead",
)


def dicom_info_from_args(args: argparse.Namespace) -> DicomInfo | None:
"""Retrieve DICOM info using edition related parser arguments."""
if not os.path.exists(args.standard_path):
print(f"Invalid standard path {args.standard_path} - aborting")
if args.revision:
edition_str = args.revision
warnings.warn(
"--revision is deprecated, use --edition instead", DeprecationWarning
)
else:
edition_str = args.edition
edition_reader = EditionReader(args.standard_path)
edition = edition_reader.get_edition(edition_str)
if edition is None:
print(f"Invalid DICOM edition {edition_str} - aborting")
return None
destination = edition_reader.get_edition_path(edition, args.recreate_json)
if destination is None:
print(f"Failed to get DICOM edition {edition_str} - aborting")
return None
return edition_reader.load_dicom_info(edition)
57 changes: 19 additions & 38 deletions dicom_validator/dump_dcm_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,11 @@
import re
import sys
from collections.abc import Iterable, Sequence
from pathlib import Path

from pydicom import config, dcmread, Dataset, DataElement
from pydicom.errors import InvalidDicomError

from dicom_validator.spec_reader.edition_reader import EditionReader
from dicom_validator.command_line_utils import dicom_info_from_args, add_edition_args
from dicom_validator.validator.dicom_info import DicomInfo


Expand All @@ -35,22 +34,22 @@ def __init__(
for uid_dict in dicom_info.dictionary.values():
self.uid_info.update(uid_dict)

tags = tags or []
self.tags: list[str] = []
for tag in tags:
match = self.tag_regex.match(tag)
if match:
self.tags.append(f"({match.group(1)},{match.group(2)})")
else:
matching = [
tag_id
for tag_id in dicom_info.dictionary
if dicom_info.dictionary[tag_id]["name"].replace(" ", "") == tag
]
if matching:
self.tags.append(matching[0])
if tags is not None:
for tag in tags:
match = self.tag_regex.match(tag)
if match:
self.tags.append(f"({match.group(1)},{match.group(2)})")
else:
print(f"{tag} is not a valid tag expression - ignoring")
matching = [
tag_id
for tag_id in dicom_info.dictionary
if dicom_info.dictionary[tag_id]["name"].replace(" ", "") == tag
]
if matching:
self.tags.append(matching[0])
else:
print(f"{tag} is not a valid tag expression - ignoring")

def print_dataset(self, dataset: Dataset) -> None:
dataset.walk(
Expand Down Expand Up @@ -144,25 +143,12 @@ def dump_directory(self, dir_path: str) -> None:

def main() -> int:
parser = argparse.ArgumentParser(
description="Dumps DICOM information dictionary from " "DICOM file using PS3.6"
description="Dumps DICOM information dictionary from DICOM file using PS3.6"
)
parser.add_argument(
"dicomfiles", help="Path(s) of DICOM files or directories to parse", nargs="+"
)
parser.add_argument(
"--standard-path",
"-src",
help="Path with the DICOM specs in docbook " "and json format",
default=str(Path.home() / "dicom-validator"),
)
parser.add_argument(
"--revision",
"-r",
help='Standard revision (e.g. "2014c"), year of '
'revision, "current" or "local" (latest '
"locally installed)",
default="current",
)
add_edition_args(parser)
parser.add_argument(
"--max-value-len",
"-ml",
Expand Down Expand Up @@ -191,15 +177,10 @@ def main() -> int:
default=False,
)
args = parser.parse_args()

edition_reader = EditionReader(args.standard_path)
destination = edition_reader.get_revision(args.revision, args.recreate_json)
if destination is None:
print(f"Failed to get DICOM edition {args.revision} - aborting")
dicom_info = dicom_info_from_args(args)
if dicom_info is None:
return 1

json_path = destination / "json"
dicom_info = EditionReader.load_dicom_info(json_path)
dumper = DataElementDumper(
dicom_info, args.max_value_len, args.show_image_data, args.show_tags
)
Expand Down
Loading