IHE XDM document packages

Read IHE XDM packages - the zip with IHE_XDM and METADATA.XML that Direct messages, SFTP drops and media carry clinical documents in - and get each document with its metadata, in Python.

An IHE XDM package - Cross-Enterprise Document Media Interchange - is a zip archive that carries clinical documents together with a description of each of them, which is how documents travel when there is no document registry between the sender and the recipient - in a Direct message, on a USB stick or a CD handed to a patient, or as a file dropped on an SFTP server. Zato opens the package and gives your service each document the package describes, with its metadata, in one call.

Inside the zip, an IHE_XDM directory holds one subdirectory per submission set - the documents one sender sent together - and each subdirectory holds the documents and a METADATA.XML file, an ebXML SubmitObjectsRequest with an entry per document stating its location, MIME type, title, patient, document codes, hash and size. Next to the directory, the root of the zip has an INDEX.HTM and a README.TXT for a person opening the package by hand, which the reading skips.

Read a package

A service is a Python class that Zato runs when something invokes it. Whatever brought the package to the service - an email attachment, a file, a REST request - the service reads the documents inside it the same way:

# -*- coding: utf-8 -*-

# Zato
from zato.server.service import Service

class XDMImport(Service):
    """ Stores each clinical document of an XDM package in a FHIR server.
    """
    name = 'documents.xdm-import'

    def handle(self) -> 'None':

        # Each document the package's metadata names, in metadata order ..
        for document in self.request.input.documents():

            self.logger.info('%s -> %s, %s, patient %s', document.file_name, document.title, document.mime_type,
                document.patient_id)

            # .. the human-readable renderings that travel alongside are not converted ..
            if document.mime_type == 'application/pdf':
                continue

            # .. and each clinical document becomes a FHIR bundle that goes to the server as a whole.
            bundle = self.ccda.to_fhir(document.data)

            client = self.fhir['FHIR.Registry']
            client.execute('', method='post', data=bundle.to_dict())

Each document has these attributes:

AttributeWhat it holds
dataThe document's bytes, as they are in the package
file_nameThe document's file name inside the package
mime_typeIts MIME type, from the metadata
sizeThe size of the document, in bytes
sourcexdm for a document read out of a package, zip for a file of a plain archive and attachment for a document that arrived on its own
titleThe document's title
patient_idThe patient the document is about, as the sender's system identifies them
class_codeThe document's class code, e.g. 34133-9 for a summary of episode note
type_codeThe document's type code
unique_idThe document's unique ID, the same wherever the document goes
creation_timeWhen the document was created, as the metadata states it
languageThe document's language code, e.g. en-US

The call opens the package, reads the metadata of every submission set and returns the documents the metadata names - a file in the package that no entry describes, a thumbnail or a note, is left out. Each document's size and SHA-1 hash are checked against what the metadata states about it, and a document that does not match raises an exception before anything is returned, so a service never acts on a package that was changed in transit.

The call is not specific to XDM. An attachment or a file that is not a zip is returned as the one document it is, with its MIME type and no metadata, and a zip that has no IHE_XDM directory returns each file inside it as a document with source set to zip - so a service written as the one above handles a bare C-CDA, a plain zip of documents and an XDM package without telling them apart.

Where packages come from

The package reaches the service in one of several ways, and in each the service calls documents() on what it received:

  1. A Direct message. The package is the attachment of an email, and an IMAP connection with its Scheduler section set to Each attachment invokes the service once per attachment, so self.request.input is the attachment. A message whose subject starts with XDM/1.0/DDM carries a package.
  2. A file. A file transfer channel watches a directory or an SFTP server that a sender drops packages into, and self.request.input is the file.
  3. A REST upload. A channel receives the zip in the request body, and read_documents from zato.common.documents.unpack reads it:
# -*- coding: utf-8 -*-

# Zato
from zato.common.documents.unpack import read_documents
from zato.server.service import Service

class XDMUpload(Service):
    """ Receives an XDM package over REST and logs the documents it names.
    """
    name = 'documents.xdm-upload'

    def handle(self) -> 'None':

        for document in read_documents(self.request.input, file_name='upload.zip'):
            self.logger.info('%s -> %s', document.file_name, document.title)

For the first case, an IMAP connection reading the inbox that packages arrive in is created under Connections > Outgoing > E-mail > IMAP in the Dashboard, with the service to invoke and Each attachment in its Scheduler row:

Metadata

Each document's attributes come from its entry in METADATA.XML - an ExtrinsicObject with slots, a name, classifications and external identifiers:

AttributeWhere it comes from
file_nameThe URI slot, which names the file relative to the metadata, with the submission set's directory
mime_typeThe mimeType attribute of the entry
titleThe Name element's LocalizedString
patient_idThe XDSDocumentEntry.patientId external identifier
class_codeThe classCode classification, scheme urn:uuid:41a5887f-8865-4c09-adf7-e362475b143a
type_codeThe typeCode classification, scheme urn:uuid:f0306f51-975f-434e-a61c-c59651d33983
unique_idThe XDSDocumentEntry.uniqueId external identifier
creation_timeThe creationTime slot
languageThe languageCode slot
sizeCounted from the document, and compared with the size slot when the metadata has one
HashThe hash slot, compared with the SHA-1 of the document when the metadata has one

A package may hold several submission sets, each in its own subdirectory with its own metadata, and the documents of all of them are returned, set by set.

XDR and XDS.b

The same metadata describes documents in the other IHE document-sharing profiles. XDR - Cross-Enterprise Document Reliable Interchange - sends the documents and their SubmitObjectsRequest in a SOAP message rather than a zip, and XDS.b registers them with a document registry that other systems query. Both are SOAP 1.2 with MTOM and the stack that IHE healthcare document exchange describes, and XDM is the same documents and metadata with no SOAP and no registry, which is why it is what travels in email and on media.

Troubleshooting

The exception says no-metadata. The zip has an IHE_XDM directory but no METADATA.XML in any of its subdirectories, or the file is there but is not XML the reading can parse. The sender's package is incomplete.

The exception says missing-file. An entry's URI slot names a file that is not in the package. The exception's file_name is the name the metadata gave.

The exception says hash-mismatch or size-mismatch. The document in the package is not the one the metadata describes - it was changed after the package was built, or the sender's system states a hash or size for a different version of the document. The message or the file stays where it was, since the service raised, and the sender is the one to ask.

All files came back as documents, with no titles. The zip has no IHE_XDM directory at its root, so it was read as a plain archive. A package whose IHE_XDM is inside another directory is not an XDM package by the profile's definition.

See also

PageWhat it covers
Direct Secure MessagingThe messages that most XDM packages arrive in
C-CDA to FHIRConverting the documents a package holds
Receiving filesPicking packages up from directories and SFTP servers
IHE healthcare document exchangeXDR and XDS.b over SOAP, with MTOM and signed SAML
IMAPReading email on a schedule, with messages or attachments on input

Learn more


Schedule a meaningful demo

Book a demo with an expert who will help you build meaningful systems that match your ambitions

"We evaluated 12 integration platforms and Zato was the only one to score 100%."

Philip Zuñiga, Assistant Professor, University of the Philippines