view Resources/Samples/Python/AutoClassify.py @ 3103:81b58b549845

back to using 'var' instead of 'let' since let is not supported by many old browsers. All variables declaration have been moved to the top of the function to better show that their scope is the function
author Alain Mazy <alain@mazy.be>
date Thu, 10 Jan 2019 10:51:36 +0100
parents 4e43e67f8ecf
children d00db444a3a3
line wrap: on
line source

#!/usr/bin/python
# -*- coding: utf-8 -*-

# Orthanc - A Lightweight, RESTful DICOM Store
# Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics
# Department, University Hospital of Liege, Belgium
# Copyright (C) 2017-2019 Osimis S.A., Belgium
#
# This program is free software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.


import argparse
import time
import os
import os.path
import sys
import RestToolbox

parser = argparse.ArgumentParser(
    description = 'Automated classification of DICOM files from Orthanc.',
    formatter_class = argparse.ArgumentDefaultsHelpFormatter)

parser.add_argument('--host', default = '127.0.0.1',
                    help = 'The host address that runs Orthanc')
parser.add_argument('--port', type = int, default = '8042',
                    help = 'The port number to which Orthanc is listening for the REST API')
parser.add_argument('--target', default = 'OrthancFiles',
                    help = 'The target directory where to store the DICOM files')
parser.add_argument('--all', action = 'store_true',
                    help = 'Replay the entire history on startup (disabled by default)')
parser.set_defaults(all = False)
parser.add_argument('--remove', action = 'store_true',
                    help = 'Remove DICOM files from Orthanc once classified (disabled by default)')
parser.set_defaults(remove = False)


def FixPath(p):
    return p.encode('ascii', errors = 'replace').translate(None, r"'\/:*?\"<>|!=").strip()

def GetTag(resource, tag):
    if ('MainDicomTags' in resource and
        tag in resource['MainDicomTags']):
        return resource['MainDicomTags'][tag]
    else:
        return 'No' + tag

def ClassifyInstance(instanceId):
    # Extract the patient, study, series and instance information
    instance = RestToolbox.DoGet('%s/instances/%s' % (URL, instanceId))
    series = RestToolbox.DoGet('%s/series/%s' % (URL, instance['ParentSeries']))
    study = RestToolbox.DoGet('%s/studies/%s' % (URL, series['ParentStudy']))
    patient = RestToolbox.DoGet('%s/patients/%s' % (URL, study['ParentPatient']))

    # Construct a target path
    a = '%s - %s' % (GetTag(patient, 'PatientID'),
                     GetTag(patient, 'PatientName'))
    b = GetTag(study, 'StudyDescription')
    c = '%s - %s' % (GetTag(series, 'Modality'),
                     GetTag(series, 'SeriesDescription'))
    d = '%s.dcm' % GetTag(instance, 'SOPInstanceUID')
    
    p = os.path.join(args.target, FixPath(a), FixPath(b), FixPath(c))
    f = os.path.join(p, FixPath(d))

    # Copy the DICOM file to the target path
    print('Writing new DICOM file: %s' % f)
    
    try:
        os.makedirs(p)
    except:
        # Already existing directory, ignore the error
        pass
    
    dcm = RestToolbox.DoGet('%s/instances/%s/file' % (URL, instanceId))
    with open(f, 'wb') as g:
        g.write(dcm)


# Parse the arguments
args = parser.parse_args()
URL = 'http://%s:%d' % (args.host, args.port)
print('Connecting to Orthanc on address: %s' % URL)

# Compute the starting point for the changes loop
if args.all:
    current = 0
else:
    current = RestToolbox.DoGet(URL + '/changes?last')['Last']

# Polling loop using the 'changes' API of Orthanc, waiting for the
# incoming of new DICOM files
while True:
    r = RestToolbox.DoGet(URL + '/changes', {
            'since' : current,
            'limit' : 4   # Retrieve at most 4 changes at once
            })

    for change in r['Changes']:
        # We are only interested interested in the arrival of new instances
        if change['ChangeType'] == 'NewInstance':
            try:
                ClassifyInstance(change['ID'])

                # If requested, remove the instance once it has been
                # properly handled by "ClassifyInstance()". Thanks to
                # the "try/except" block, the instance is not removed
                # if the "ClassifyInstance()" function fails.
                if args.remove:
                    RestToolbox.DoDelete('%s/instances/%s' % (URL, change['ID']))

            except:
                print('Unable to write instance %s to the disk' % change['ID'])

    current = r['Last']

    if r['Done']:
        print('Everything has been processed: Waiting...')
        time.sleep(1)