Esempio n. 1
0
def main():

    # Specify import statements
    import requests
    import sys
    import importlib
    import shutil
    import logging
    import hashlib
    import UserInterface
    import time

    # Retrieve variables from invoking function
    selector = importlib.import_module(
        os.path.basename(sys.modules['__main__'].__file__).split('.')[0])

    # Specify branch to download
    branch = 'master'
    logging.debug('user name {}'.format(os.getenv('username')))
    os.chdir(os.path.dirname(__file__))
    logging.debug('current directory is {}'.format(os.getcwd()))

    # Get branch content
    try:
        if selector.token != '':
            r = requests.get(
                selector.api + '/contents?ref=' + branch,
                headers={'Authorization': 'token {}'.format(selector.token)})
        else:
            r = requests.get(selector.api + '/contents?ref=' + branch)

        file_list = r.json()

    except requests.ConnectionError:
        logging.exception('Could not access GitHub repository')
        raise

    # If local is empty, prompt user to select the location
    if selector.local == '':
        browser = UserInterface.CommonDialog()
        local = browser.folder_browser('Select folder location for scripts:')
    else:
        local = selector.local

    # Clear directory
    if os.path.exists(local):
        if os.path.isfile(local):
            logging.error('This is a file, not directory {}'.format(local))
        elif os.path.isdir(local):
            # Leave the master directory in place, and remove the contents
            os.chdir('../..')
            while os.listdir(local):
                for filename in os.listdir(local):
                    file_path = os.path.join(local, filename)
                    try:
                        if os.path.isfile(file_path) or os.path.islink(
                                file_path):
                            os.unlink(file_path)
                            time.sleep(1)
                        elif os.path.isdir(file_path):
                            shutil.rmtree(file_path, ignore_errors=True)
                            time.sleep(1)
                    except Exception as e:
                        logging.debug('Failed to delete %s. Reason: %s' %
                                      (file_path, e))
    else:
        os.mkdir(local)

    if os.path.exists(local):
        logging.info('Local directory is: {}'.format(local))
    else:
        logging.warning('Local directory failed to create: {}'.format(local))
        sys.exit('Exiting')

    # Loop through folders in branch, creating folders and pulling content
    for l in file_list:
        if l.get('type'):
            if l['type'] == u'dir':
                if selector.token != '':
                    r = requests.get(selector.api + '/contents' + l['path'] +
                                     '?ref=' + branch,
                                     headers={
                                         'Authorization':
                                         'token {}'.format(selector.token)
                                     })
                else:
                    r = requests.get(selector.api + '/contents' + l['path'] +
                                     '?ref=' + branch)

                sublist = r.json()
                for s in sublist:
                    file_list.append(s)

                if not os.path.exists(os.path.join(local, l['path'])):
                    os.mkdir(os.path.join(local, l['path']))

    # Update progress bar text and length
    bar = UserInterface.ProgressBar('Downloading files', 'Update Progress',
                                    len(file_list) * 2)

    # Loop through files in branch, downloading each
    for l in file_list:
        bar.update('Downloading {}'.format(l['path']))
        if l['type'] == u'file':
            if l.get('download_url'):
                logging.info('Downloading {} to {}'.format(
                    l['download_url'], os.path.join(local, l['path'])))
                if os.path.exists(os.path.join(local, l['path'])):
                    os.remove(os.path.join(local, l['path']))
                if selector.token != '':
                    r = requests.get(l['download_url'],
                                     headers={
                                         'Authorization':
                                         'token {}'.format(selector.token)
                                     })
                else:
                    r = requests.get(l['download_url'])

                open(os.path.join(local, l['path']), 'wb').write(r.content)

    # Loop through files again, verifying
    passed = True
    for l in file_list:
        bar.update('Verifying Hashes')
        if l['type'] == u'file':
            if l.get('download_url'):

                fh = open(os.path.join(local, l['path']), 'rb')
                content = fh.read()
                fh.close()
                sha = hashlib.sha1(
                    bytearray('blob {}\0'.format(len(content)), 'utf8') +
                    content).hexdigest()

                if l['sha'] == sha:
                    logging.info('Hash {} verified: {}'.format(
                        l['path'], l['sha']))
                else:
                    logging.warning('Hash {} incorrect: {} != {}'.format(
                        l['path'], l['sha'], sha))
                    passed = False

    # Show success message
    bar.close()
    if passed:
        UserInterface.MessageBox(
            'Script download and checksum verification successful', 'Success')
    else:
        UserInterface.WarningBox('Scripts download, but verification failed',
                                 'Warning')
def main():
    ''' Attempt to load and optimize a patient '''

    import sys
    import csv
    import os
    import connect
    from OptimizationOperations import optimize_plan
    from collections import namedtuple
    import UserInterface

    # Plan optimization parameters

    OptimizationParameters = {
        "InitialMaxIt": 50,
        "InitialIntIt": 10,
        "SecondMaxIt": 30,
        "SecondIntIt": 15,
        "DoseDim1": 0.5,
        "DoseDim2": 0.4,
        "DoseDim3": 0.3,
        "DoseDim4": 0.2,
        "NIterations": 12
    }

    # Open the csv delimited file containing the list of patients to be reoptimized
    # Ensure that the first row is a header for the columns
    Row = namedtuple('Row', ('FirstName', 'LastName', 'PatientID', 'Case',
                             'PlanName', 'BeamsetName'))
    browser = UserInterface.CommonDialog()
    filecsv = browser.open_file('Select a plan list file',
                                'CSV Files (*.csv)|*.csv')
    if filecsv != '':
        with open(filecsv, 'r') as f:
            r = csv.reader(f, delimiter=',')
            r.next()  # Skip header
            rows = [Row(*l) for l in r]

        file_name = r'output.txt'
        path = r'\\uwhis.hosp.wisc.edu\ufs\UWHealth\RadOnc\ShareAll\RayScripts\dev_logs'
        output_file = os.path.join(path, file_name)
        file_object = open(output_file, 'w')
        output_message = "PatientID" + "\tPlan Name" + "\tBeamSet Name" + "\tStatus\n"
        file_object.write(output_message)
        file_object.close()
        # Header was skipped.  Start with rows[0], the first data line in the csv
        i = 0
        db = connect.get_current("PatientDB")
        # TODO : look for existing, then add a beamset.
        for r in rows:
            # for i in range(1,10):
            last_name = r.LastName
            first_name = r.FirstName
            patient_id = r.PatientID
            plan_name = r.PlanName
            beamset_name = r.BeamsetName
            case_name = r.Case
            # Select patient based on name
            patient_info = db.QueryPatientInfo(
                Filter={
                    'FirstName': '^{0}$'.format(first_name),
                    'LastName': '^{0}$'.format(last_name),
                    'PatientID': '^{0}$'.format(patient_id)
                })
            if len(
                    patient_info
            ) != 1:  # If no (or more than one) patient matches the criteria, exit the script
                print("No patient named {0} {1} found in the database".format(
                    first_name, last_name))
                sys.exit()
            #try:
            patient = db.LoadPatient(PatientInfo=patient_info[0])
            case = patient.Cases[case_name]
            case.SetCurrent()
            plan = case.TreatmentPlans[plan_name]
            plan.SetCurrent()
            beamset = plan.BeamSets[beamset_name]
            beamset.SetCurrent()
            optimize_plan(patient, case, plan, beamset,
                          **OptimizationParameters)
            patient.Save()
            file_object = open("output.txt", 'a')
            output_message = patient_id + "\t" + plan_name + "\t" + beamset_name + "\tsuccess\n"
            file_object.write(output_message)
            file_object.close()
            #except:
            #    file_object = open("output.txt", 'a')
            #    output_message = patient_id + "\t" + plan_name + "\t" + beamset_name + "\tFAIL\n"
            #    file_object.write(output_message)
            #    file_object.close()
            i += 1
Esempio n. 3
0
def main():

    # Get current date, time and initialize variables
    now = datetime.datetime.now()
    name = ''
    mrn = ''
    size = []
    res = []
    path = ''
    pad = True
    status = None
    patient_db = None
    machine_db = None

    # If running from within RayStation, write to temp folder and import
    try:

        # Import RayStation packages
        import connect
        import UserInterface

        # Connect to RayStation DB
        logging.debug('Attempting to connect to RsyStation DB')
        machine_db = connect.get_current('MachineDB')
        patient_db = connect.get_current('PatientDB')

        # If an existing patient is loaded, warn the user before saving
        try:
            patient = connect.get_current('Patient')
            case = connect.get_current('Case')
            box = UserInterface.WarningeBox(
                'An existing patient is loaded, and will be saved and closed')
            patient.Save()

        except Exception:
            logging.info('No patient is loaded')

        # Start script status window
        status = UserInterface.ScriptStatus(steps=[
            'Enter Phantom Dimensions', 'Generate Temporary CT Files',
            'Import Files into RayStation', 'Set Imaging Equipment',
            'Create External Contour', 'Export CT (optional)'
        ],
                                            docstring=__doc__,
                                            help=__help__)
        status.next_step(
            text=
            'For this step, enter the desired phantom details in the displayed input box.'
        )

        # Display input dialog and retrieve phantom size
        inputs = UserInterface.InputDialog(inputs={
            'a':
            'Enter phantom name:',
            'b':
            'Enter phantom ID:',
            'c':
            'Enter number of voxels in IEC X,Z,Y:',
            'd':
            'Enter resolution in IEC X,Z,Y (mm):'
        },
                                           required=['a', 'b', 'c', 'd'],
                                           initial={
                                               'a':
                                               'Water Phantom',
                                               'b':
                                               '{0}{1:0>2}{2:0>2}'.format(
                                                   now.year, now.month,
                                                   now.day),
                                               'c':
                                               '600, 400, 600',
                                               'd':
                                               '1, 1, 1'
                                           })

        # Wait a second to make sure the script selector is displayed before the input dialog
        time.sleep(1)
        logging.debug('Displaying input dialog')
        response = inputs.show()

        # Check if dialog was closed
        if response == {}:
            logging.warning('Input dialog closed')
            status.finish(text='The input dialog was closed, script cancelled')

        else:
            logging.debug('Parsing inputs and generating temp folder')
            name = response['a'].strip()
            mrn = response['b'].strip()
            size = map(int, response['c'].split(','))
            res = map(int, response['d'].split(','))

            status.next_step(
                text=
                'Generating temporary CT files based on provided dimensions...'
            )
            path = tempfile.mkdtemp()
            logging.debug('Temporary folder generated at {}'.format(path))

    # Prompt for name, ID, image size and resolution (in mm), IEC [X,Z,Y]
    except (ImportError, OSError, SystemError):
        logging.info(
            'Likely running outside RayStation, prompting user to enter info via raw_input()'
        )
        name = raw_input('Enter phantom name: ').strip()
        mrn = raw_input('Enter phantom ID: ').strip()
        size = map(
            int,
            raw_input('Enter number of voxels in IEC X,Z,Y (600, 400, 600): ').
            split(','))
        res = map(
            int,
            raw_input('Enter mm resolution in IEC X,Z,Y (1, 1, 1): ').split(
                ','))
        path = raw_input('Enter path to write CT to: ').strip()
        if not os.path.exists(path):
            logging.debug(
                'Provided path does not exist, creating {}'.format(path))
            os.mkdir(path)

    # Only continue if inputs were provided
    if name != '' and mrn != '' and len(size) == 3 and len(res) == 3:

        # Start timer
        tic = time.time()

        # Pad X/Z dimensions by a voxel (will be air)
        if pad:
            logging.debug('Padding image in X/Z dimensions')
            size[0] += 2
            size[1] += 2

        # Create new dict, and add basic image attributes
        logging.debug('Initializing DICOM header')
        ds = pydicom.dataset.Dataset()
        ds.file_meta = pydicom.dataset.Dataset()
        ds.file_meta.TransferSyntaxUID = '1.2.840.10008.1.2'
        ds.file_meta.ImplementationClassUID = '1.2.40.0.13.1.1'
        ds.file_meta.ImplementationVersionName = 'dcm4che-2.0'
        ds.SpecificCharacterSet = 'ISO_IR 100'
        ds.file_meta.MediaStorageSOPClassUID = '1.2.840.10008.5.1.4.1.1.2'
        ds.Modality = 'CT'
        ds.SOPClassUID = ds.file_meta.MediaStorageSOPClassUID
        ds.is_little_endian = True
        ds.is_implicit_VR = True
        ds.RescaleIntercept = -1024
        ds.RescaleSlope = 1
        ds.InstanceCreationDate = '{0}{1:0>2}{2:0>2}'.format(
            now.year, now.month, now.day)
        ds.InstanceCreationTime = '{0:0>2}{1:0>2}{2:0>2}'.format(
            now.hour, now.minute, now.second)
        ds.StudyDate = '{0}{1:0>2}{2:0>2}'.format(now.year, now.month, now.day)
        ds.StudyTime = '{0:0>2}{1:0>2}{2:0>2}'.format(now.hour, now.minute,
                                                      now.second)
        ds.AcquisitionDate = '{0}{1:0>2}{2:0>2}'.format(
            now.year, now.month, now.day)
        ds.AcquisitionTime = '{0:0>2}{1:0>2}{2:0>2}'.format(
            now.hour, now.minute, now.second)
        ds.ImageType = 'ORIGINAL\PRIMARY\AXIAL'
        ds.Manufacturer = 'pydicom'
        ds.ManufacturerModelName = 'CreateReferenceCT'
        ds.SoftwareVersion = '1.0'
        ds.SeriesDescription = 'Uniform Phantom'
        ds.PatientName = name
        ds.PatientID = mrn
        ds.SliceThickness = res[2]
        ds.StudyInstanceUID = pydicom.uid.generate_uid()
        ds.SeriesInstanceUID = pydicom.uid.generate_uid()
        ds.FrameOfReferenceUID = pydicom.uid.generate_uid()
        ds.PatientPosition = 'HFS'
        ds.ImageOrientationPatient = [1, 0, 0, 0, 1, 0]
        ds.ImagePositionPatient = [
            -((size[0] - 0.5) * res[0]) / 2, -res[1] / 2,
            ((size[2] - 0.5) * res[2]) / 2
        ]
        ds.ImagesInAcquisition = size[2]
        ds.SamplesPerPixel = 1
        ds.PhotometricInterpretation = 'MONOCHROME2'
        ds.Rows = size[1]
        ds.Columns = size[0]
        ds.PixelSpacing = [res[0], res[1]]
        ds.BitsAllocated = 16
        ds.BitsStored = 16
        ds.HighBit = 15
        ds.PixelRepresentation = 0

        # Create image
        logging.debug('Initializing rectangular phantom image')
        if pad:
            img = numpy.zeros(shape=(size[1], size[0]), dtype=numpy.uint16)
            for i in range(1, size[1] - 1):
                for j in range(1, size[0] - 1):
                    img[i, j] = 1024

        else:
            img = numpy.ones(shape=(size[1], size[0]),
                             dtype=numpy.uint16) * 1024

        ds.PixelData = img.tostring()

        # Only display progress bar
        if isinstance(status, UserInterface.ScriptStatus):
            bar = UserInterface.ProgressBar(text='Writing CT files',
                                            steps=size[2])

        else:
            bar = None

        # Loop through CT Images
        for i in range(size[2]):

            # Generate unique IDs
            ds.file_meta.MediaStorageSOPInstanceUID = pydicom.uid.generate_uid(
            )
            ds.SOPInstanceUID = ds.file_meta.MediaStorageSOPInstanceUID

            # Set position info for this image
            ds.SliceLocation = -((size[2] - 1) * res[2]) / 2 + i * res[2]
            ds.ImagePositionPatient[2] = -ds.SliceLocation
            ds.InstanceNumber = i + 1

            # Write CT image
            if isinstance(bar, UserInterface.ProgressBar):
                bar.update('Writing image ct_{0:0>3}.dcm'.format(i + 1))

            if path != '':
                logging.debug('Writing image {0}/ct_{1:0>3}.dcm'.format(
                    path, i + 1))
                ds.save_as(
                    os.path.normpath('{0}/ct_{1:0>3}.dcm'.format(path, i + 1)))

        if isinstance(bar, UserInterface.ProgressBar):
            bar.close()

        # If in RayStation, import DICOM files
        if isinstance(status,
                      UserInterface.ScriptStatus) and patient_db is not None:

            status.next_step(
                text='Importing the temporary CT into RayStation...')
            logging.debug(
                'Executing ImportPatientFromPath against {}'.format(path))
            patient_db.ImportPatientFromPath(Path=path,
                                             Patient={'Name': name},
                                             SeriesFilter={},
                                             ImportFilters=[])

            patient = connect.get_current('Patient')
            case = connect.get_current('Case')
            examination = connect.get_current('Examination')
            logging.info('Import successful, patient name: {}, MRN: {}'.format(
                patient.Name, patient.PatientID))

            # Set imaging equipment
            if machine_db is not None:
                try:
                    import clr
                    clr.AddReference('System.Collections')
                    import System.Collections.Generic
                    ct_dict = machine_db.GetCtImagingSystemsNameAndCommissionTime(
                    )
                    e = ct_dict.GetEnumerator()
                    e.MoveNext()
                    status.next_step(
                        'Setting imaging equipment to {}...'.format(
                            e.Current.Key))
                    logging.debug('Setting imaging equipment to {}'.format(
                        e.Current.Key))
                    examination.EquipmentInfo.SetImagingSystemReference(
                        ImagingSystemName=e.Current.Key)

                except Exception as error:
                    logging.warning(str(error))

            # Create external ROI
            status.next_step(text='Generating External Contour...')
            logging.debug('Executing PatientModel.CreateRoi for External')
            external = case.PatientModel.CreateRoi(Name='External',
                                                   Color='Blue',
                                                   Type='External',
                                                   TissueName='',
                                                   RbeCellTypeName=None,
                                                   RoiMaterial=None)

            logging.debug('Executing CreateExternalGeometry for External')
            external.CreateExternalGeometry(Examination=examination,
                                            ThresholdLevel=None)
            logging.debug('Saving patient')
            patient.Save()

            # Prompt user to export
            logging.debug('Displaying export question box')
            status.next_step(
                text=
                'At this step, you can choose to export the phantom CT and structure set. '
                + 'Answer Yes or No in the displayed message box.')
            answer = UserInterface.QuestionBox(
                'Do you wish to export the phantom to a folder?')
            if answer.yes:
                logging.debug(
                    'User chose Yes to export CT and structure set, now displaying folder browser'
                )
                common = UserInterface.CommonDialog()
                export = common.folder_browser('Select a folder to export to:')

                try:
                    status.update_text('Exporting CT and structure set...')
                    logging.debug(
                        'Executing ScriptableDicomExport to export to {}'.
                        format(export))
                    case.ScriptableDicomExport(
                        ExportFolderPath=export,
                        Examinations=[examination.Name],
                        RtStructureSetsForExaminations=[examination.Name],
                        DicomFilter='',
                        IgnorePreConditionWarnings=True)

                except Exception as error:
                    logging.warning(str(error))

            # Finish up
            logging.debug(
                'Cleanup started, deleting temporary folder {}'.format(path))
            shutil.rmtree(path, ignore_errors=True)
            status.finish(
                text=
                'Script execution successful. Note, the phantom material was not set to water.'
                +
                ' If you plan on running other QA scripts, you may want to do so.'
            )

        logging.debug(
            'CreateReferenceCT finished successfully in {:.3f} seconds'.format(
                time.time() - tic))

    else:
        logging.warning('Patient name, MRN, size, or resolution invalid')
    else:
        calc = False
        export = False

    # Append steps
    for m in machines:
        machine = machine_db.GetTreatmentMachine(machineName=m, lockMode=None)
        for q in machine.PhotonBeamQualities:
            status.add_step('Create {} {} MV plans'.format(m, q.NominalEnergy))

        status.add_step('Create {} Electrons plan'.format(m))

    # Define the export location
    if 'Calculate dose' in response['g'] and 'Export dose' in response['g']:
        common = UserInterface.CommonDialog()
        path = common.folder_browser(
            'Select the path to export RT Dose files to (or cancel to skip export):'
        )

        if path == '':
            logging.warning('Folder not selected, export will be skipped')
            export = False

        else:
            logging.info('Export path selected: {}'.format(path))
            export = True

    # Start timer
    tic = time.time()