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

[MRG] Add convenience function Event.encoded_dataset() #888

Merged
merged 10 commits into from
Nov 17, 2023
Next Next commit
Add convenience function to Event for encoding a conformant DICOM Fil…
…e Format bytestream
  • Loading branch information
scaramallion committed Nov 17, 2023
commit 1e0ff9e0d63feea92306c544ddb3cd7728b0dad2
2 changes: 2 additions & 0 deletions docs/changelog/v2.1.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Enhancements
:class:`~pynetdicom.service_class.QueryRetrieveServiceClass` (:issue:`878`)
* Added support for :class:`Inventory Query/Retrieve Service Class
<pynetdicom.service_class.InventoryQueryRetrieveServiceClass>` (:issue:`879`)
* Added :meth:`pynetdicom.event.Event.encoded_dataset` to simplify writing
the raw encoded dataset to file

Changes
.......
Expand Down
15 changes: 5 additions & 10 deletions docs/examples/storage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -157,21 +157,16 @@ multiple C-STORE requests, depending on the size of the datasets:

.. code-block:: python

from pydicom.filewriter import write_file_meta_info

import uuid
from pynetdicom import AE, evt, AllStoragePresentationContexts

# Implement a handler for evt.EVT_C_STORE
def handle_store(event):
"""Handle a C-STORE request event."""
with open(event.request.AffectedSOPInstanceUID, 'wb') as f:
# Write the preamble and prefix
f.write(b'\x00' * 128)
f.write(b'DICM')
# Encode and write the File Meta Information
write_file_meta_info(f, event.file_meta)
# Write the encoded dataset
f.write(event.request.DataSet.getvalue())
with open(f"{uuid.uuid4()}", 'wb') as f:
# Write the preamble, prefix, file meta information
# and encoded dataset to `f`
f.write(event.encoded_dataset())

# Return a 'Success' status
return 0x0000
Expand Down
32 changes: 13 additions & 19 deletions docs/tutorials/create_scp.rst
Original file line number Diff line number Diff line change
Expand Up @@ -397,11 +397,10 @@ complex code:

.. code-block:: python
:linenos:
:emphasize-lines: 1,3,12,14-18,21-28,32
:emphasize-lines: 1-2,11,13-17,19-23,27

import os

from pydicom.filewriter import write_file_meta_info
import uuid
from pathlib import Path

from pynetdicom import (
AE, debug_logger, evt, AllStoragePresentationContexts,
Expand All @@ -418,15 +417,11 @@ complex code:
# Unable to create output dir, return failure status
return 0xC001

# We rely on the UID from the C-STORE request instead of decoding
fname = os.path.join(storage_dir, event.request.AffectedSOPInstanceUID)
with open(fname, 'wb') as f:
# Write the preamble, prefix and file meta information elements
f.write(b'\x00' * 128)
f.write(b'DICM')
write_file_meta_info(f, event.file_meta)
# Write the raw encoded dataset
f.write(event.request.DataSet.getvalue())
path = Path(storage_dir) / f"{uuid.uuid4()}"
with path.open('wb') as f:
# Write the preamble, prefix, file meta information elements
# and the raw encoded dataset to `f`
f.write(event.encoded_dataset())

return 0x0000

Expand All @@ -441,12 +436,11 @@ complex code:

ae.start_server(("127.0.0.1", 11112), block=True, evt_handlers=handlers)

We've modified the handler to write the preamble and prefix to file,
encode and write the file meta information elements using *pydicom's*
:func:`~pydicom.filewriter.write_file_meta_info` function, then finally write
the encoded dataset using the :attr:`raw dataset
<dimse_primitives.C_STORE.DataSet>` received directly from the C-STORE
request via the ``event.request`` attribute.
We've modified the handler to use :meth:`~pynetdicom.events.Event.encoded_dataset`,
which writes the preamble, prefix, file meta information elements and the
:attr:`raw dataset<dimse_primitives.C_STORE.DataSet>` received in the C-STORE
request directly to file. If you need separate access to the raw encoded dataset
then you can get it via the ``event.request.DataSet`` attribute.

The second change we've made is to demonstrate how extra parameters can be
passed to the handler by binding using a 3-tuple rather than a 2-tuple. The
Expand Down
9 changes: 4 additions & 5 deletions pynetdicom/dsutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from io import BytesIO
import logging
from pathlib import Path
from typing import Optional, List, Tuple
import zlib

from pydicom import Dataset
Expand Down Expand Up @@ -128,7 +127,7 @@ def decode(

def encode(
ds: Dataset, is_implicit_vr: bool, is_little_endian: bool, deflated: bool = False
) -> Optional[bytes]:
) -> bytes | None:
"""Encode a *pydicom* :class:`~pydicom.dataset.Dataset` `ds`.

.. versionchanged:: 1.5
Expand Down Expand Up @@ -182,7 +181,7 @@ def encode(
return bytestring


def pretty_dataset(ds: Dataset, indent: int = 0, indent_char: str = " ") -> List[str]:
def pretty_dataset(ds: Dataset, indent: int = 0, indent_char: str = " ") -> list[str]:
"""Return a list of pretty dataset strings.

.. versionadded:: 1.5
Expand Down Expand Up @@ -270,7 +269,7 @@ def pretty_element(elem: DataElement) -> str:
)


def split_dataset(path: Path) -> Tuple[Dataset, int]:
def split_dataset(path: Path) -> tuple[Dataset, int]:
"""Return the file meta elements and the offset to the start of the dataset

.. versionadded:: 2.0
Expand All @@ -288,7 +287,7 @@ def split_dataset(path: Path) -> Tuple[Dataset, int]:
no File Meta is present.
"""

def _not_group_0002(tag: BaseTag, VR: Optional[str], length: int) -> bool:
def _not_group_0002(tag: BaseTag, VR: str | None, length: int) -> bool:
"""Return True if the tag is not in group 0x0002, False otherwise."""
return tag.group != 2

Expand Down
Loading