def check_external(roi_list):
    if any(roi.OfRoi.Type == 'External' for roi in roi_list):
        logging.debug('External contour designated')
        return True
    else:
        logging.debug('No external contour designated')
        connect.await_user_input(
            'No External contour type designated. Give a contour an External type and continue script.'
        )
        if any(roi.OfRoi.Type == 'External' for roi in roi_list):
            logging.debug(
                'No external contour designated after prompt recommend exit')
            return False
def main():

    # Get current patient, case, and machine DB
    machine_db = connect.get_current('MachineDB')
    try:
        patient = connect.get_current('Patient')
        case = connect.get_current('Case')
        patient.Save()

    except Exception:
        UserInterface.WarningBox('This script requires a patient to be loaded')
        sys.exit('This script requires a patient to be loaded')

    # Start script status
    status = UserInterface.ScriptStatus(steps=[
        'Verify CT density table and external are set',
        'Enter script runtime options'
    ],
                                        docstring=__doc__,
                                        help=__help__)

    # Confirm a CT density table, boxes, and external contour was set
    status.next_step(
        text=
        'Prior to execution, the script will make sure that a CT density table, External, '
        +
        'and Box_1/Box_2 contours are set for the current plan. Also, at least one contour must be '
        + 'overridden to water.')
    time.sleep(1)

    examination = connect.get_current('Examination')
    if examination.EquipmentInfo.ImagingSystemReference is None:
        connect.await_user_input(
            'The CT imaging system is not set. Set it now, then continue the script.'
        )
        patient.Save()

    else:
        patient.Save()

    external = False
    bounds = [-30, 30, 0, 40, -30, 30]
    for r in case.PatientModel.RegionsOfInterest:
        if r.Type == 'External':
            external = True
            b = case.PatientModel.StructureSets[
                examination.Name].RoiGeometries[r.Name].GetBoundingBox()
            bounds = [b[0].x, b[1].x, b[0].y, b[1].y, b[0].z, b[1].z]
Beispiel #3
0
def prompt_machine_change(adapted_beam_set):
    """ Prompt machine change to Agility 
    
    We have optimized on StrctAgility. Not scriptable.
    """

    #Prompt user to change machine from StrctAgility to Agility
    pauseMsg = "Change treatment machine to " + FINAL_MACHINE + " \n\n"
    pauseMsg += "\t1. Click \"OK\" on this message\n"
    pauseMsg += "\t2. Click \"Edit adapted plan\" in Plan Setup \n"
    pauseMsg += "\t3. Change Treatment Machine to \"" + FINAL_MACHINE + "\" \n"
    pauseMsg += "\t4. In Scripting side panel click play icon near bottom of window"

    rsl.await_user_input(pauseMsg)

    # Warn if machine not changed
    if (adapted_beam_set.MachineReference.MachineName == FINAL_MACHINE):
        print("Machine changed successfully")
    else:
        m = "Treatment machine not changed to " + FINAL_MACHINE + " \n"
        m = m + "Change machine, perform dose calculation\nand rescale to beamset prescription"
        RmhMessageBox.message(m, "CHANGE TREATMENT MACHINE")
def main():

    # Get current patient, case, and machine DB
    machine_db = connect.get_current('MachineDB')
    try:
        patient = connect.get_current('Patient')
        case = connect.get_current('Case')

    except Exception:
        UserInterface.WarningBox('This script requires a patient to be loaded')
        sys.exit('This script requires a patient to be loaded')

    # Start script status
    status = UserInterface.ScriptStatus(steps=['Verify CT density table and external are set',
                                               'Select folder to import DICOM RT plans from',
                                               'Select folder to export calculated dose to',
                                               'Choose import overrides and calculation options',
                                               'Import, re-calculate, and export plans'],
                                        docstring=__doc__,
                                        help=__help__)

    # Confirm a CT density table and external contour was set
    status.next_step(text='Prior to execution, the script will make sure that a CT density table and External ' +
                          'contour are set for the current plan. These are required for dose calculation.')
    examination = connect.get_current('Examination')
    if examination.EquipmentInfo.ImagingSystemReference is None:
        connect.await_user_input('The CT imaging system is not set. Set it now, then continue the script.')
        patient.Save()

    else:
        patient.Save()

    external = False
    for r in case.PatientModel.RegionsOfInterest:
        if r.Type == 'External':
            external = True
Beispiel #5
0
                        'Matching protocol ElementTag found for {}'.format(
                            input_dialog.values['i']))
                    break
        else:
            logging.debug('No orders in protocol')
            use_orders = False

        # Find RS targets
        plan_targets = []
        for r in case.PatientModel.RegionsOfInterest:
            if r.OrganData.OrganType == 'Target':
                plan_targets.append(r.Name)
        # Add user threat: empty PTV list.
        if not plan_targets:
            connect.await_user_input(
                "The target list is empty." +
                " Please apply type PTV to the targets and continue.")
            for r in case.PatientModel.RegionsOfInterest:
                if r.OrganData.OrganType == 'Target':
                    plan_targets.append(r.Name)
        if not plan_targets:
            status.finish('Script cancelled, inputs were not supplied')
            sys.exit('Script cancelled')

        status.next_step(text="Matching all structures to the current list.",
                         num=1)

        protocol_targets = []
        missing_contours = []

        # Build second dialog
Beispiel #6
0
def main():

    # Get current patient, case, exam, plan, and beamset
    try:
        patient = connect.get_current('Patient')
        case = connect.get_current('Case')
        exam = connect.get_current('Examination')

    except Exception:
        UserInterface.WarningBox('This script requires a patient to be loaded')
        sys.exit('This script requires a patient to be loaded')

    try:
        plan = connect.get_current('Plan')
        beamset = connect.get_current('BeamSet')

    except Exception:
        logging.debug(
            'A plan and/or beamset is not loaded; plan export options will be disabled'
        )
        plan = None
        beamset = None

    # Start timer
    tic = time.time()

    # Initialize script status
    status = UserInterface.ScriptStatus(steps=[
        'Approve and save structures/plan',
        'Select DICOM data and destination', 'Apply filters and export'
    ],
                                        docstring=__doc__,
                                        help=__help__)

    # Check if plan and/or structure set is approved
    status.next_step(
        text=
        'Prior to export, this script will check if the plan is approved, and will ask if want to '
        + 'do so prior to approval if not.')
    time.sleep(1)
    patient.Save()
    ignore = False
    if beamset is not None:
        try:
            if plan.Review is None or plan.Review.ApprovalStatus != 'Approved':
                plan_approved = False

            else:
                plan_approved = True

        except Exception:
            plan_approved = False

        if not plan_approved:
            approve = UserInterface.QuestionBox(
                'The selected plan is not currently approved. Would you like to '
                + 'approve it prior to export?', 'Approve Plan')
            if approve.yes:
                ui = connect.get_current('ui')
                ui.TitleBar.MenuItem['Plan Evaluation'].Click()
                ui.TitleBar.MenuItem['Plan Evaluation'].Popup.MenuItem[
                    'Plan Evaluation'].Click()
                ui.TabControl_ToolBar.Approval.Select()
                connect.await_user_input(
                    'Approve the plan now, then continue the script')

            else:
                logging.warning(
                    'The user chose to export the plan without approval')
                ignore = True

        try:
            if case.PatientModel.StructureSets[
                    exam.Name].ApprovedStructureSets[
                        0].Review.ApprovalStatus != 'Approved':
                struct_approved = False

            else:
                struct_approved = True
Beispiel #7
0
        except Exception:
            struct_approved = False

        if not struct_approved:
            approve = UserInterface.QuestionBox(
                'The selected structure set is not currently approved. Would you '
                + 'like to approve it prior to export?',
                'Approve Structure Set')
            if approve.yes:
                ui = connect.get_current('ui')
                ui.TitleBar.MenuItem['Patient Modeling'].Click()
                ui.TitleBar.MenuItem['Patient Modeling'].Popup.MenuItem[
                    'Structure Definition'].Click()
                ui.TabControl_ToolBar.Approval.Select()
                connect.await_user_input(
                    'Approve the structure set now, then continue the script')

            else:
                logging.warning(
                    'The user chose to export the structure set without approval'
                )
                ignore = True

    # Prompt user for DICOM export details
    status.next_step(
        text=
        'In the dialog window that appears, check each DICOM destination that you would like to '
        +
        'export to, what should be exported, and if a beamset is loaded, what treatment delivery '
        + 'system to convert to.')
def main():
    import UserInterface
    import connect
    import logging
    import sys

    # These are the techniques associated with billing codes in the clinic
    # they will be imported
    availabletechniques = [
        'Static MLC -- 2D', 'Static NoMLC -- 2D', 'Electron -- 2D',
        'Static MLC -- 3D', 'Static NoMLC -- 3D', 'Electron -- 3D',
        'FiF MLC -- 3D', 'Static PRDR MLC -- 3D', 'SnS MLC -- IMRT',
        'SnS PRDR MLC -- IMRT', 'Conformal Arc -- 3D', 'VMAT Arc -- IMRT'
    ]
    supportedRStechniques = ['SMLC', 'DynamicArc']

    dialog = UserInterface.InputDialog(
        inputs={
            'Site': 'Enter a Site name, e.g. BreL',
            'Technique': 'Select Treatment Technique (Billing)'
        },
        datatype={'Technique': 'combo'},
        initial={'Technique': 'Select'},
        options={'Technique': availabletechniques},
        required=['Site', 'Technique'])
    # Show the dialog
    print dialog.show()

    site_name = dialog.values['Site']
    inputtechnique = dialog.values['Technique']

    try:
        patient = connect.get_current('Patient')
        case = connect.get_current('Case')
        exam = connect.get_current('Examination')
        plan = connect.get_current('Plan')
        beamset = connect.get_current("BeamSet")

    except Exception:
        UserInterface.WarningBox(
            'This script requires a Beam Set to be loaded')
        sys.exit('This script requires a Beam Set to be loaded')

    #
    # Electrons, 3D, and VMAT Arcs are all that are supported.  Reject plans that aren't
    technique = beamset.DeliveryTechnique
    #
    # Oddly enough, Electrons are DeliveryTechnique = 'SMLC'
    if technique not in supportedRStechniques:
        raise IOError(
            "Technique unsupported, manually name beams according to clinical convention."
        )

    # While loop variable definitions
    beam_index = 0
    patient_position = beamset.PatientPosition
    logging.debug(
        'Renaming and adding set up fields to Beam Set with name {}, patient position {}, technique {}'
        .format(beamset.DicomPlanLabel, beamset.PatientPosition,
                beamset.DeliveryTechnique))
    #
    # HFS Beam Naming
    if patient_position == 'HeadFirstSupine':
        for b in beamset.Beams:
            try:
                gantry_angle = int(b.GantryAngle)
                couch_angle = int(b.CouchAngle)
                gantry_angle_string = str(int(gantry_angle))
                couch_angle_string = str(int(couch_angle))
                #
                # Determine if the type is an Arc or SMLC
                # Name arcs as #_Arc_<Site>_<Direction>_<Couch>
                if technique == 'DynamicArc':
                    arc_direction = b.ArcRotationDirection
                    if arc_direction == 'Clockwise':
                        arc_direction_string = 'CW'
                    else:
                        arc_direction_string = 'CCW'

                    # Based on convention for billing, e.g. "1 CCW VMAT -- IMRT"
                    # set the beam_description
                    beam_description = (str(beam_index + 1) + ' ' +
                                        arc_direction_string + ' ' +
                                        inputtechnique)
                    if couch_angle == 0:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_Arc')
                    else:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_Arc' + '_c' +
                                              couch_angle_string.zfill(3))
                else:
                    # Based on convention for billing, e.g. "1 SnS PRDR MLC -- IMRT"
                    # set the beam_description
                    beam_description = str(beam_index +
                                           1) + ' ' + inputtechnique
                    if couch_angle != 0:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_g' +
                                              gantry_angle_string.zfill(3) +
                                              'c' +
                                              couch_angle_string.zfill(3))
                    elif gantry_angle == 180:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_PA'
                    elif gantry_angle > 180 and gantry_angle < 270:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RPO'
                    elif gantry_angle == 270:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RLAT'
                    elif gantry_angle > 270 and gantry_angle < 360:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RAO'
                    elif gantry_angle == 0:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_AP'
                    elif gantry_angle > 0 and gantry_angle < 90:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LAO'
                    elif gantry_angle == 90:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LLAT'
                    elif gantry_angle > 90 and gantry_angle < 180:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LPO'

                # Set the beamset names and description according to the convention above
                b.Name = standard_beam_name
                b.Description = beam_description
                beam_index += 1
            except Exception:
                UserInterface.WarningBox(
                    'Error occured in setting names of beams')
                sys.exit('Error occurred in setting names of beams')
        connect.await_user_input(
            'Please go to Plan Design>Plan Setup and use Copy Setup to ensure there are 4 Setup beams'
        )
        #
        # Set-Up Fields
        try:
            #
            # AP set-up field
            beamset.PatientSetup.SetupBeams[0].Name = "SetUp AP"
            beamset.PatientSetup.SetupBeams[0].Description = "SetUp AP"
            beamset.PatientSetup.SetupBeams[0].GantryAngle = "0.0"
            beamset.PatientSetup.SetupBeams[0].Segments[0].DoseRate = "5"

            #
            # Rt Lateral set-up field
            beamset.PatientSetup.SetupBeams[1].Name = "SetUp RtLat"
            beamset.PatientSetup.SetupBeams[1].Description = "SetUp RtLat"
            beamset.PatientSetup.SetupBeams[1].GantryAngle = "270.0"
            beamset.PatientSetup.SetupBeams[1].Segments[0].DoseRate = "5"

            #
            # Lt Lateral set-up field
            beamset.PatientSetup.SetupBeams[2].Name = "SetUp LtLat"
            beamset.PatientSetup.SetupBeams[2].Description = "SetUp LtLat"
            beamset.PatientSetup.SetupBeams[2].GantryAngle = "90.0"
            beamset.PatientSetup.SetupBeams[2].Segments[0].DoseRate = "5"
            #
            # Cone-Beam CT set-up field
            try:
                beamset.PatientSetup.SetupBeams[3].Name = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].Description = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].GantryAngle = "0.0"
                beamset.PatientSetup.SetupBeams[3].Segments[0].DoseRate = "5"
            except:
                connect.await_user_input(
                    'Pretty Please go to Plan Design>Plan Setup and copy any Setup Beam then continue script'
                )
                beamset.PatientSetup.SetupBeams[3].Name = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].Description = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].GantryAngle = "0.0"
                beamset.PatientSetup.SetupBeams[3].Segments[0].DoseRate = "5"
        except:
            raise IOError(
                'Please select Create Set Up Beams in Edit Plan and Rerun script'
            )

            # Address the Head-first prone position
    elif patient_position == 'HeadFirstProne':
        for b in beamset.Beams:
            try:
                gantry_angle = int(b.GantryAngle)
                couch_angle = int(b.CouchAngle)
                gantry_angle_string = str(int(gantry_angle))
                couch_angle_string = str(int(couch_angle))
                #
                # Determine if the type is an Arc or SMLC
                # Name arcs as #_Arc_<Site>_<Direction>_<Couch>
                if technique == 'DynamicArc':
                    arc_direction = b.ArcRotationDirection
                    if arc_direction == 'Clockwise':
                        arc_direction_string = 'CW'
                    else:
                        arc_direction_string = 'CCW'

                    # Based on convention for billing, e.g. "1 CCW VMAT -- IMRT"
                    # set the beam_description
                    beam_description = (str(beam_index + 1) + ' ' +
                                        arc_direction_string + ' ' +
                                        inputtechnique)
                    if couch_angle == 0:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_Arc')
                    else:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_Arc' + '_c' +
                                              couch_angle_string.zfill(3))
                else:
                    # Based on convention for billing, e.g. "1 SnS PRDR MLC -- IMRT"
                    # set the beam_description
                    beam_description = str(beam_index +
                                           1) + ' ' + inputtechnique
                    if couch_angle != 0:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_g' +
                                              gantry_angle_string.zfill(3) +
                                              'c' +
                                              couch_angle_string.zfill(3))
                    elif gantry_angle == 180:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_AP'
                    elif gantry_angle > 180 and gantry_angle < 270:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LAO'
                    elif gantry_angle == 270:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LLAT'
                    elif gantry_angle > 270 and gantry_angle < 360:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LPO'
                    elif gantry_angle == 0:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_PA'
                    elif gantry_angle > 0 and gantry_angle < 90:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RPO'
                    elif gantry_angle == 90:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RLAT'
                    elif gantry_angle > 90 and gantry_angle < 180:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RAO'
                # Set the beamset names and description according to the convention above
                b.Name = standard_beam_name
                b.Description = beam_description
                beam_index += 1
            except Exception:
                UserInterface.WarningBox(
                    'Error occured in setting names of beams')
                sys.exit('Error occurred in setting names of beams')
        connect.await_user_input(
            'Please go to Plan Design>Plan Setup and use Copy Setup to ensure there are 4 Setup beams'
        )
        #
        # Set-Up fields
        try:
            #
            # PA set-up field
            beamset.PatientSetup.SetupBeams[0].Name = "SetUp PA"
            beamset.PatientSetup.SetupBeams[0].Description = "SetUp PA"
            beamset.PatientSetup.SetupBeams[0].GantryAngle = "0.0"
            beamset.PatientSetup.SetupBeams[0].Segments[0].DoseRate = "5"

            #
            # Rt Lateral set-up field
            beamset.PatientSetup.SetupBeams[1].Name = "SetUp RtLat"
            beamset.PatientSetup.SetupBeams[1].Description = "SetUp RtLat"
            beamset.PatientSetup.SetupBeams[1].GantryAngle = "90.0"
            beamset.PatientSetup.SetupBeams[1].Segments[0].DoseRate = "5"

            #
            # Lt Lateral set-up field
            beamset.PatientSetup.SetupBeams[2].Name = "SetUp LtLat"
            beamset.PatientSetup.SetupBeams[2].Description = "SetUp LtLat"
            beamset.PatientSetup.SetupBeams[2].GantryAngle = "270.0"
            beamset.PatientSetup.SetupBeams[2].Segments[0].DoseRate = "5"

            #
            # Cone-Beam CT set-up field
            try:
                beamset.PatientSetup.SetupBeams[3].Name = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].Description = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].GantryAngle = "0.0"
                beamset.PatientSetup.SetupBeams[3].Segments[0].DoseRate = "5"
            except:
                connect.await_user_input(
                    'Pretty Please go to Plan Design>Plan Setup and copy any Setup Beam then continue script'
                )
                beamset.PatientSetup.SetupBeams[3].Name = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].Description = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].GantryAngle = "0.0"
                beamset.PatientSetup.SetupBeams[3].Segments[0].DoseRate = "5"
        except:
            raise IOError(
                'Please select Create Set Up Beams in Edit Plan and Rerun script'
            )
            # Address the Feet-first supine position
    elif patient_position == 'FeetFirstSupine':
        for b in beamset.Beams:
            try:
                gantry_angle = int(b.GantryAngle)
                couch_angle = int(b.CouchAngle)
                gantry_angle_string = str(int(gantry_angle))
                couch_angle_string = str(int(couch_angle))
                #
                # Determine if the type is an Arc or SMLC
                # Name arcs as #_Arc_<Site>_<Direction>_<Couch>
                if technique == 'DynamicArc':
                    arc_direction = b.ArcRotationDirection
                    if arc_direction == 'Clockwise':
                        arc_direction_string = 'CW'
                    else:
                        arc_direction_string = 'CCW'

                    # Based on convention for billing, e.g. "1 CCW VMAT -- IMRT"
                    # set the beam_description
                    beam_description = (str(beam_index + 1) + ' ' +
                                        arc_direction_string + ' ' +
                                        inputtechnique)
                    if couch_angle == 0:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_Arc')
                    else:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_Arc' + '_c' +
                                              couch_angle_string.zfill(3))
                else:
                    # Based on convention for billing, e.g. "1 SnS PRDR MLC -- IMRT"
                    # set the beam_description
                    beam_description = str(beam_index +
                                           1) + ' ' + inputtechnique
                    if couch_angle != 0:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_g' +
                                              gantry_angle_string.zfill(3) +
                                              'c' +
                                              couch_angle_string.zfill(3))
                    elif gantry_angle == 180:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_PA'
                    elif gantry_angle > 180 and gantry_angle < 270:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LPO'
                    elif gantry_angle == 270:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LLAT'
                    elif gantry_angle > 270 and gantry_angle < 360:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LAO'
                    elif gantry_angle == 0:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_AP'
                    elif gantry_angle > 0 and gantry_angle < 90:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RAO'
                    elif gantry_angle == 90:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RLAT'
                    elif gantry_angle > 90 and gantry_angle < 180:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RPO'
                # Set the beamset names and description according to the convention above
                b.Name = standard_beam_name
                b.Description = beam_description
                beam_index += 1
            except Exception:
                UserInterface.WarningBox(
                    'Error occured in setting names of beams')
                sys.exit('Error occurred in setting names of beams')
        connect.await_user_input(
            'Please go to Plan Design>Plan Setup and use Copy Setup to ensure there are 4 Setup beams'
        )
        #
        # Set-Up Fields
        try:
            # AP set-up field
            beamset.PatientSetup.SetupBeams[0].Name = "SetUp AP"
            beamset.PatientSetup.SetupBeams[0].Description = "SetUp AP"
            beamset.PatientSetup.SetupBeams[0].GantryAngle = "0.0"
            beamset.PatientSetup.SetupBeams[0].Segments[0].DoseRate = "5"

            # Rt Lateral set-up field
            beamset.PatientSetup.SetupBeams[1].Name = "SetUp RtLat"
            beamset.PatientSetup.SetupBeams[1].Description = "SetUp RtLat"
            beamset.PatientSetup.SetupBeams[1].GantryAngle = "90.0"
            beamset.PatientSetup.SetupBeams[1].Segments[0].DoseRate = "5"

            # Lt Lateral set-up field
            beamset.PatientSetup.SetupBeams[2].Name = "SetUp LtLat"
            beamset.PatientSetup.SetupBeams[2].Description = "SetUp LtLat"
            beamset.PatientSetup.SetupBeams[2].GantryAngle = "270.0"
            beamset.PatientSetup.SetupBeams[2].Segments[0].DoseRate = "5"

            #
            # Cone-Beam CT set-up field
            try:
                beamset.PatientSetup.SetupBeams[3].Name = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].Description = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].GantryAngle = "0.0"
                beamset.PatientSetup.SetupBeams[3].Segments[0].DoseRate = "5"
            except:
                connect.await_user_input(
                    'Pretty Please go to Plan Design>Plan Setup and copy any Setup Beam then continue script'
                )
                beamset.PatientSetup.SetupBeams[3].Name = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].Description = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].GantryAngle = "0.0"
                beamset.PatientSetup.SetupBeams[3].Segments[0].DoseRate = "5"
        except:
            raise IOError(
                'Please select Create Set Up Beams in Edit Plan and Rerun script'
            )

            # Address the Feet-first prone position
    elif patient_position == 'FeetFirstProne':
        for b in beamset.Beams:
            try:
                gantry_angle = int(b.GantryAngle)
                couch_angle = int(b.CouchAngle)
                gantry_angle_string = str(int(gantry_angle))
                couch_angle_string = str(int(couch_angle))
                #
                # Determine if the type is an Arc or SMLC
                # Name arcs as #_Arc_<Site>_<Direction>_<Couch>
                if technique == 'DynamicArc':
                    arc_direction = b.ArcRotationDirection
                    if arc_direction == 'Clockwise':
                        arc_direction_string = 'CW'
                    else:
                        arc_direction_string = 'CCW'

                    # Based on convention for billing, e.g. "1 CCW VMAT -- IMRT"
                    # set the beam_description
                    beam_description = (str(beam_index + 1) + ' ' +
                                        arc_direction_string + ' ' +
                                        inputtechnique)
                    if couch_angle == 0:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_Arc')
                    else:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_Arc' + '_c' +
                                              couch_angle_string.zfill(3))
                else:
                    # Based on convention for billing, e.g. "1 SnS PRDR MLC -- IMRT"
                    # set the beam_description
                    beam_description = str(beam_index +
                                           1) + ' ' + inputtechnique
                    if couch_angle != 0:
                        standard_beam_name = (str(beam_index + 1) + '_' +
                                              site_name + '_g' +
                                              gantry_angle_string.zfill(3) +
                                              'c' +
                                              couch_angle_string.zfill(3))
                    elif gantry_angle == 180:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_AP'
                    elif gantry_angle > 180 and gantry_angle < 270:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RAO'
                    elif gantry_angle == 270:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RLAT'
                    elif gantry_angle > 270 and gantry_angle < 360:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_RPO'
                    elif gantry_angle == 0:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_PA'
                    elif gantry_angle > 0 and gantry_angle < 90:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LPO'
                    elif gantry_angle == 90:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LLAT'
                    elif gantry_angle > 90 and gantry_angle < 180:
                        standard_beam_name = str(beam_index +
                                                 1) + '_' + site_name + '_LAO'
                # Set the beamset names and description according to the convention above
                b.Name = standard_beam_name
                b.Description = beam_description
                beam_index += 1
            except Exception:
                UserInterface.WarningBox(
                    'Error occured in setting names of beams')
                sys.exit('Error occurred in setting names of beams')
        connect.await_user_input(
            'Please go to Plan Design>Plan Setup and use Copy Setup to ensure there are 4 Setup beams'
        )
        try:
            # PA set-up field
            beamset.PatientSetup.SetupBeams[0].Name = "SetUp PA"
            beamset.PatientSetup.SetupBeams[0].Description = "SetUp PA"
            beamset.PatientSetup.SetupBeams[0].GantryAngle = "0.0"
            beamset.PatientSetup.SetupBeams[0].Segments[0].DoseRate = "5"

            # Rt Lateral set-up field
            beamset.PatientSetup.SetupBeams[1].Name = "SetUp RtLat"
            beamset.PatientSetup.SetupBeams[1].Description = "SetUp RtLat"
            beamset.PatientSetup.SetupBeams[1].GantryAngle = "270.0"
            beamset.PatientSetup.SetupBeams[1].Segments[0].DoseRate = "5"

            # Lt Lateral set-up field
            beamset.PatientSetup.SetupBeams[2].Name = "SetUp LtLat"
            beamset.PatientSetup.SetupBeams[2].Description = "SetUp LtLat"
            beamset.PatientSetup.SetupBeams[2].GantryAngle = "90.0"
            beamset.PatientSetup.SetupBeams[2].Segments[0].DoseRate = "5"
            #
            # Cone-Beam CT set-up field
            try:
                beamset.PatientSetup.SetupBeams[3].Name = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].Description = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].GantryAngle = "0.0"
                beamset.PatientSetup.SetupBeams[3].Segments[0].DoseRate = "5"
            except:
                connect.await_user_input(
                    'Pretty Please go to Plan Design>Plan Setup and copy any Setup Beam then continue script'
                )
                beamset.PatientSetup.SetupBeams[3].Name = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].Description = "SetUp CBCT"
                beamset.PatientSetup.SetupBeams[3].GantryAngle = "0.0"
                beamset.PatientSetup.SetupBeams[3].Segments[0].DoseRate = "5"
        except:
            raise IOError(
                'Please select Create Set Up Beams in Edit Plan and Rerun script'
            )
    else:
        raise IOError(
            "Patient Orientation Unsupported.. Manual Beam Naming Required")
Beispiel #9
0
        status.next_step(
            text='Transfer plan dose computed, setting up dose comparison.')

        # This appears to confuse RayStation. Perhaps when the API is a little better integrated with the UI?
        # ui = connect.get_current('ui')
        # try:
        #     ui.TitleBar.MenuItem['Plan Evaluation'].Click()
        #     ui.TitleBar.MenuItem['Plan Evaluation'].Popup.MenuItem['Plan Evaluation'].Click()
        #     ui.TabControl_ToolBar.TabItem._Approval.Select()
        #     ui.ToolPanel.TabItem['Scripting'].Select()
        # except:
        #     logging.debug('Failed to operate the user interface through script again. Proceeding')

        connect.await_user_input(
            'Compare the transfer beamset: {} and parent beamset {}'.format(
                daughter_beamset.DicomPlanLabel, parent_beamset_name) +
            ' dose distributions and continue, if acceptable approve this beamset.'
        )
        status.next_step(text='Transfer plan accepted')

        # Sending report

    # For multiple beamsets, RayStation will not send a beamset to the raygateway for a transferred plan
    # when a single beamset is approved but the plan is not. Thus, we loop though the beamsets again now that
    # all transfer beamsets are approved.
    for b in parent_plan.BeamSets:

        parent_beamset_name = b.DicomPlanLabel
        parent_plan_iDMS_name = parent_plan.Name + ':' + parent_beamset_name
        new_bs_name = parent_beamset_name[:8] + '_Tr' + daughter_machine[-3:]
        daughter_beamset = PlanOperations.find_beamset(
Beispiel #10
0
    try:
        ui = connect.get_current('ui')
        ui.TitleBar.MenuItem['Patient Modeling'].Button_Patient_Modeling.Click(
        )
    except:
        logging.debug("Could not click on the plan Design MenuItem")

    shoulder_poi_left = 'Block_L_Inf_POI'
    shoulder_poi_right = 'Block_R_Inf_POI'
    logging.debug('Points exist? {}'.format(
        StructureOperations.exists_poi(
            case=case, pois=[shoulder_poi_right, shoulder_poi_left])))

    if any(StructureOperations.exists_poi(case=case, pois=shoulder_poi_left)):
        connect.await_user_input(
            ('Ensure the point {} is at the left acromial-clavicular joint' +
             ' and continue script.').format(shoulder_poi_left))
    else:
        case.PatientModel.CreatePoi(Examination=exam,
                                    Point=par_beam_set.iso['Position'],
                                    Volume=0,
                                    Name=shoulder_poi_left,
                                    Color='Green',
                                    VisualizationDiameter=2,
                                    Type='Control')
        connect.await_user_input(
            ('Place the point {} at the left acromial-clavicular joint' +
             ' and continue script.'.format(shoulder_poi_left)))

    shoulder_left_position = case.PatientModel.StructureSets[
        exam.Name].PoiGeometries[shoulder_poi_left]
                                                DoseAlgorithm='CCDose')
                            time.sleep(1)
                            patient.Save()
                            time.sleep(1)

            # Loop through DLG offset iterations
            if export:
                for i in iters:

                    # Prompt user to set DLG offset
                    counter += 1
                    logging.debug(
                        'Prompting user to update {} {} MV DLG offset to {} mm'
                        .format(m, e, i))
                    connect.await_user_input(
                        'Set Mobius3D {} {} MV DLG offset to {} mm, and set dose calculation '
                        + 'resolution to {} mm'.format(m, e, i, res))

                    # Loop through gaps
                    for g in gaps:

                        # Loop through offsets
                        for o in offsets:

                            # Query beamset, then export and load
                            info = plan.QueryBeamSetInfo(
                                Filter={
                                    'Name': '{}mm-{}mm'.format(g * 10, o * 10)
                                })
                            if not info:
                                logging.warning(
def find_targets(case):
    """
    Find all structures with type 'Target' within the current case. Return the matches as a list
    :param case: Current RS Case
    :return: plan_targets # A List of targets
    """

    # Find RS targets
    plan_targets = []
    for r in case.PatientModel.RegionsOfInterest:
        if r.OrganData.OrganType == 'Target':
            plan_targets.append(r.Name)
    # Add user threat: empty PTV list.
    if not plan_targets:
        connect.await_user_input("The target list is empty." +
                                 " Please apply type PTV to the targets and continue.")
        for r in case.PatientModel.RegionsOfInterest:
            if r.OrganData.OrganType == 'Target':
                plan_targets.append(r.Name)
    if plan_targets:
        return plan_targets
    else:
        sys.exit('Script cancelled')


def check_structure_exists(case, structure_name, roi_list=None, option='Check'):
    """
    Verify if a structure with the exact name specified exists or not
    :param case: Current RS case
    :param structure_name: the name of the structure to be confirmed
    :param roi_list: complete list of all available ROI's.
Beispiel #13
0
    # Capture the current list of ROI's to avoid saving over them in the future
    rois = case.PatientModel.StructureSets[examination.Name].RoiGeometries

    # Capture the current list of POI's to avoid a crash
    pois = case.PatientModel.PointsOfInterest

    # Find all targets
    plan_targets = []
    for r in case.PatientModel.RegionsOfInterest:
        if r.OrganData.OrganType == 'Target':
            plan_targets.append(r.Name)
    # Add user threat: empty target list.
    if not plan_targets:
        connect.await_user_input(
            "The target list is empty." +
            " Please apply type PTV to the targets and continue.")
        for r in case.PatientModel.RegionsOfInterest:
            if r.OrganData.OrganType == 'Target':
                plan_targets.append(r.Name)
    if not plan_targets:
        status.finish('Script cancelled, targets were not supplied')
        sys.exit('Script cancelled')
    # TODO: Add handling for empty targets or geometries

    # Look for the sim point, if not create a point
    sim_point_found = any(poi.Name == 'SimFiducials' for poi in pois)
    if sim_point_found:
        logging.warning("POI SimFiducials Exists")
        status.next_step(
            text="SimFiducials Point found, ensure that it is placed properly")
                                                   Description='',
                                                   GantryAngle=0,
                                                   CouchAngle=0,
                                                   CollimatorAngle=0)
                    beam.SetBolus(BolusName='')

                # Display prompt reminding user to set EDW angles
                time.sleep(1)
                patient.Save()
                plan.SetCurrent()
                refset.SetCurrent()
                status.update_text(
                    'Manually set the EDW beams for {} according to the beam name. '
                    + 'Then continue the script.'.format(m))
                connect.await_user_input(
                    'Manually set EDWs for {}. Then continue the script.'.
                    format(m))
                time.sleep(1)
                patient.Save()

            else:
                refset = plan.LoadBeamSet(BeamSetInfo=info[0])

            # Loop through each SSD
            for s in ssds:

                # Check if beam set exists
                info = plan.QueryBeamSetInfo(
                    Filter={'Name': 'EDW {} cm SSD'.format(s)})
                if not info:
                          'contour are set for the current plan. These are required for dose calculation.')
    examination = connect.get_current('Examination')
    if examination.EquipmentInfo.ImagingSystemReference is None:
        connect.await_user_input('The CT imaging system is not set. Set it now, then continue the script.')
        patient.Save()

    else:
        patient.Save()

    external = False
    for r in case.PatientModel.RegionsOfInterest:
        if r.Type == 'External':
            external = True

    if not external:
        connect.await_user_input('No external contour was found. Generate an external contour, then continue ' +
                                 'the script.')
        patient.Save()

    # Define path to search for RT PLANS
    status.next_step(text='For this step, select a folder containing one or more DICOM RT Plans to import, then ' +
                          'click OK. the plans may be from the same patient, or different patients.')
    common = UserInterface.CommonDialog()
    import_path = common.folder_browser('Select the path containing DICOM RT Plans to import:')
    if import_path == '':
        logging.info('Folder not selected, import will be skipped')

    else:
        logging.info('Import folder set to {}'.format(import_path))

    # Define path to export RT PLAN/DOSE to
    status.next_step(text='Next, select a folder to export the resulting DICOM RT dose volumes to, then click OK. ' +
Beispiel #16
0
def main():
    # Get current patient, case, exam, and plan
    # note that the interpreter handles a missing plan as an Exception
    patient = GeneralOperations.find_scope(level='Patient')
    case = GeneralOperations.find_scope(level='Case')
    exam = GeneralOperations.find_scope(level='Examination')
    plan = GeneralOperations.find_scope(level='Plan')
    beamset = GeneralOperations.find_scope(level='BeamSet')
    ui = GeneralOperations.find_scope(level='ui')
    # TODO put in more sophisticated InvalidOperationException Catch here.
    try:
        ui.TitleBar.MenuItem[
            'Plan Optimization'].Button_Plan_Optimization.Click()
    except:
        logging.debug('Unable to change viewing windows')

    # Institution specific plan names and dose grid settings
    fine_grid_names = ['_SBR_']
    fine_grid_size = 0.15
    coarse_grid_names = ['_THI_', '_VMA_', '_3DC_', '_BST_', '_DCA_']
    coarse_grid_size = 0.2
    rename_beams = True
    simfid_test = True
    external_test = True
    grid_test = True
    # Let the statements below change as needed
    tomo_couch_test = False
    check_lateral_pa = False
    cps_test = False
    # Set up the workflow steps.
    steps = []
    if 'Tomo' not in beamset.DeliveryTechnique and beamset.Modality != 'Electrons':
        if check_lateral_pa:
            steps.append('Check Laterality')
        steps.append('Rename Beams')
        steps.append('Check for external structure integrity')
        steps.append('Check SimFiducials have coordinates')
        steps.append('Check the dose grid size')
        steps.append('Check for control Point Spacing')
        steps.append('Compute Dose if neccessary')
        steps.append('Set DSP')
        steps.append('Round MU')
        steps.append('Round Jaws')
        steps.append('Recompute Dose')
        cps_test = True

    if 'Tomo' in beamset.DeliveryTechnique:
        steps.append('Rename Beams')
        steps.append('Check for external structure integrity')
        steps.append('Check Tomo Couch position relative to isocenter')
        steps.append('Check SimFiducials have coordinates')
        steps.append('Check the dose grid size')
        steps.append('Compute Dose if neccessary')
        steps.append('Set DSP')
        tomo_couch_test = True

    if beamset.Modality == 'Electrons':
        steps.append('Rename Beams')
        steps.append('Check for external structure integrity')
        steps.append('Check SimFiducials have coordinates')
        steps.append('Check the dose grid size')
        steps.append('Compute Dose if neccessary')
        steps.append('Set DSP')

    status = UserInterface.ScriptStatus(steps=steps,
                                        docstring=__doc__,
                                        help=__help__)
    status.next_step('Checking beam names')

    if check_lateral_pa:
        # Check the lateral PA for clearance
        for b in beamset.Beams:
            change_gantry = BeamOperations.check_pa(plan=plan, beam=b)
            logging.debug('Recommended change for {} is {}'.format(
                b.Name, change_gantry))
        # BeamOperations.check_clearance(beamset=beamset)
        status.next_step('Checked field orientations')

    if rename_beams:
        # Rename the beams
        BeamOperations.rename_beams()
        status.next_step('Renamed Beams, checking external integrity')

    # EXTERNAL OVERLAP WITH COUCH OR SUPPORTS
    if external_test:
        external_error = True
        while external_error:
            error = PlanQualityAssuranceTests.external_overlap_test(
                patient, case, exam)
            if error == 'No support structures':
                logging.critical(
                    'Evaluation of overlap with External and Support Structures not possible '
                    + 'due to no structures having type Support')
                external_error = False
            elif len(error) != 0:
                connect.await_user_input(
                    'Eliminate overlap of patient external with support structures'
                    + ' (hint: use the Couch Removal tool on the external)')
            else:
                external_error = False
        status.next_step('Reviewed external')

    # TOMO COUCH TEST
    if tomo_couch_test:
        tomo_couch_error = False
        couch_name = 'TomoCouch'
        couch = PlanQualityAssuranceTests.tomo_couch_check(
            case=case,
            exam=exam,
            beamset=beamset,
            tomo_couch_name=couch_name,
            limit=2.0,
            shift=True)
        if not couch.valid:
            try:
                connect.await_user_input(couch.error)
                if not couch.valid:
                    x_shift = couch.calculated_lateral_shift()
                    logging.info('Moving {} by {}'.format(couch_name, x_shift))
                    StructureOperations.translate_roi(case=case,
                                                      exam=exam,
                                                      roi=couch_name,
                                                      shifts={
                                                          'x': x_shift,
                                                          'y': 0,
                                                          'z': 0
                                                      })
                plan.SetDefaultDoseGrid(
                    VoxelSize={
                        'x': coarse_grid_size,
                        'y': coarse_grid_size,
                        'z': coarse_grid_size
                    })
                status.next_step(
                    'TomoTherapy couch corrected for lateral shift')
            except:
                tomo_couch_error = True
                status.next_step(
                    'TomoTherapy couch could not be corrected, likely due to approved structures.'
                )

        else:
            tomo_couch_error = False
            status.next_step(
                'TomoTherapy couch checked for correct lateral positioning')

    # SIMFIDUCIAL TEST
    if simfid_test:
        fiducial_point = 'SimFiducials'
        fiducial_error = True
        while fiducial_error:
            error = PlanQualityAssuranceTests.simfiducial_test(
                case=case, exam=exam, poi=fiducial_point)
            if len(error) != 0:
                connect.await_user_input('Error in localization point: ' +
                                         '{}\n'.format(error))
            else:
                fiducial_error = False
        status.next_step('Reviewed SimFiducials')

    # GRID SIZE TEST
    if grid_test:
        fine_grid_error = PlanQualityAssuranceTests.gridsize_test(
            beamset=beamset,
            plan_string=fine_grid_names,
            nominal_grid_size=fine_grid_size)
        coarse_grid_error = PlanQualityAssuranceTests.gridsize_test(
            beamset=beamset,
            plan_string=coarse_grid_names,
            nominal_grid_size=coarse_grid_size)
        if len(fine_grid_error) != 0:
            logging.warning(
                'Dose grid check returned an error {}'.format(fine_grid_error))
            plan.SetDefaultDoseGrid(VoxelSize={
                'x': fine_grid_size,
                'y': fine_grid_size,
                'z': fine_grid_size
            })
            logging.info('Grid size was changed for SBRT-type plan')
        elif len(coarse_grid_error) != 0:
            logging.warning('Dose grid check returned an error {}'.format(
                coarse_grid_error))
            plan.SetDefaultDoseGrid(
                VoxelSize={
                    'x': coarse_grid_size,
                    'y': coarse_grid_size,
                    'z': coarse_grid_size
                })
            logging.info('Grid size was changed for Normal-type plan')
        status.next_step('Reviewed Dose Grid')

    # CONTROL POINT SPACING TEST
    if cps_test:
        cps_error = PlanQualityAssuranceTests.cps_test(beamset, nominal_cps=2)
        if len(cps_error) != 0:
            sys.exit(cps_error)
        status.next_step(
            'Reviewed Control Point Spacing, computing dose if neccessary')

    if beamset.Modality == 'Photons':
        dose_algorithm = 'CCDose'
        if 'Tomo' in beamset.DeliveryTechnique:
            # TODO: Better exception handling here.
            try:
                beamset.ComputeDose(ComputeBeamDoses=True,
                                    DoseAlgorithm=dose_algorithm,
                                    ForceRecompute=False)
                status.next_step('Recomputed Dose, finding DSP')
            except Exception:
                status.next_step(
                    'Dose recomputation unneccessary, finding DSP')
                logging.info('Beamset {} did not need to be recomputed'.format(
                    beamset.DicomPlanLabel))
            # Set the DSP for the plan and recompute dose to force an update of the DSP
            BeamOperations.set_dsp(plan=plan, beam_set=beamset)
            beamset.ComputeDose(ComputeBeamDoses=True,
                                DoseAlgorithm=dose_algorithm,
                                ForceRecompute=True)
            status.next_step('DSP set. Script complete')
        else:
            # TODO: Better exception handling here.
            try:
                beamset.ComputeDose(ComputeBeamDoses=True,
                                    DoseAlgorithm=dose_algorithm,
                                    ForceRecompute=False)
                status.next_step('Recomputed Dose, finding DSP')
            except Exception as e:
                logging.debug(u'Message is {}'.format(e.Message))
                try:
                    if 'Dose has already been computed (with the current parameters)' in e.Message:
                        status.next_step(
                            'Dose re-computation unnecessary, finding DSP')
                        logging.info(
                            'Beamset {} did not need to be recomputed'.format(
                                beamset.DicomPlanLabel))
                    else:
                        logging.exception(u'{}'.format(e.Message))
                        sys.exit(u'{}'.format(e.Message))
                except:
                    logging.exception(u'{}'.format(e.Message))
                    sys.exit(u'{}'.format(e.Message))

            # Set the DSP for the plan
            BeamOperations.set_dsp(plan=plan, beam_set=beamset)
            status.next_step('Set DSP, rounding MU')

            # Round MU
            # The Autoscale hides in the plan optimization hierarchy. Find the correct index.
            indx = PlanOperations.find_optimization_index(
                plan=plan, beamset=beamset, verbose_logging=False)
            plan.PlanOptimizations[indx].AutoScaleToPrescription = False
            BeamOperations.round_mu(beamset)
            status.next_step('Rounded MU, Rounding jaws')

            # Round jaws to nearest mm
            logging.debug('Checking for jaw rounding')
            BeamOperations.round_jaws(beamset=beamset)
            status.next_step('Jaws Rounded.')

            # Recompute dose
            status.next_step('Recomputing Dose')
            # Compute Dose with new DSP, and recommended history settings (mainly to force a DSP update)
            try:
                beamset.ComputeDose(ComputeBeamDoses=True,
                                    DoseAlgorithm=dose_algorithm,
                                    ForceRecompute=True)
            except Exception as e:
                logging.debug(' error type is {}, with e = {}'.format(
                    type(e), e))
            status.next_step('Script Complete')

    if beamset.Modality == 'Electrons':
        dose_algorithm = 'ElectronMonteCarlo'
        # TODO: Better exception handling here.
        try:
            # Try a quick run
            if not beamset.FractionDose.DoseValues.IsClinical:
                beamset.AccurateDoseAlgorithm.MonteCarloHistoriesPerAreaFluence = 10000
                status.next_step(
                    'Computing dose with small number of histories')
                beamset.ComputeDose(ComputeBeamDoses=True,
                                    DoseAlgorithm=dose_algorithm,
                                    ForceRecompute=False)
        except Exception:
            status.next_step('Dose was clinical, no need for recompute')
            logging.info('Beamset {} did not need to be recomputed'.format(
                beamset.DicomPlanLabel))
        # Set the DSP and TODO: add rx surface
        BeamOperations.set_dsp(plan=plan,
                               beam_set=beamset,
                               percent_rx=98.,
                               method='Centroid')
        status.next_step('DSP set, checking statistics')
        mc_histories = 500000
        # Make sure electron monte carlo statistical uncertainty is clinical
        emc_result = BeamOperations.check_emc(beamset,
                                              stat_limit=0.005,
                                              histories=mc_histories)
        # If the test returns an insufficient uncertainty, change the number of histories
        if emc_result.bool is False:
            beamset.AccurateDoseAlgorithm.MonteCarloHistoriesPerAreaFluence = emc_result.hist
        # Autoscale must be turned off to round the MU.
        # Round MU
        indx = PlanOperations.find_optimization_index(plan=plan,
                                                      beamset=beamset,
                                                      verbose_logging=False)
        plan.PlanOptimizations[indx].AutoScaleToPrescription = False
        BeamOperations.round_mu(beamset)
        status.next_step('Rounded MU, recomputing doses')
        # Compute Dose with new DSP, and recommended history settings (mainly to force a DSP update)
        beamset.ComputeDose(ComputeBeamDoses=True,
                            DoseAlgorithm=dose_algorithm,
                            ForceRecompute=True)
        status.next_step('Script Complete')

    logcrit('Final Dose Script Run Successfully')
Beispiel #17
0
                .format(Optimization_Iteration, current_objective_function,
                        previous_objective_function))
            previous_objective_function = current_objective_function

        # Finish with a Reduce OAR Dose Optimization
        if segment_weight:
            status.next_step('Running Segment weight only optimization')
            report_inputs[
                'time_segment_weight_initial'] = datetime.datetime.now()
            # Uncomment when segment-weight based co-optimization is supported
            if cooptimization:
                logging.warning(
                    "Co-optimized segment weight-based optimization is" +
                    " not supported by RaySearch at this time.")
                connect.await_user_input(
                    "Segment-weight optimization with composite optimization is not supported "
                    + "by RaySearch at this time")

            else:
                for ts in treatment_setup_settings:
                    for beams in ts.BeamSettings:
                        if 'SegmentOpt' in beams.OptimizationTypes:
                            beams.EditBeamOptimizationSettings(
                                OptimizationTypes=["SegmentMU"])
                plan.PlanOptimizations[OptIndex].RunOptimization()
                logging.info(
                    'optimize_plan: Current total objective function value at iteration {} is {}'
                    .format(
                        Optimization_Iteration, plan_optimization.Objective.
                        FunctionValue.FunctionValue))
            report_inputs['time_segment_weight_final'] = datetime.datetime.now(
Beispiel #18
0
import connect
import logging
import sys


def check_localization(case, exam, create=False, confirm=False):
    # Look for the sim point, if not create a point
    # Capture the current list of POI's to avoid a crash
    pois = case.PatientModel.PointsOfInterest
    sim_point_found = any(poi.Type == 'LocalizationPoint' for poi in pois)

    if sim_point_found:
        if confirm:
            logging.info("POI SimFiducials Exists")
            connect.await_user_input('Ensure Correct placement of the SimFiducials Point and continue script.')
            return True
        else:
            return True
    else:
        if create and not sim_point_found:
            case.PatientModel.CreatePoi(Examination=exam,
                                        Point={'x': 0,
                                               'y': 0,
                                               'z': 0},
                                        Volume=0,
                                        Name="SimFiducials",
                                        Color="Green",
                                        Type="LocalizationPoint")
            connect.await_user_input('Ensure Correct placement of the SimFiducials Point and continue script.')
            return True
        else:
Beispiel #19
0
def reduce_oar_dose(plan_optimization):
    """
    Function will search the objective list and sort by target and oar generates
    then executes the reduce_oar command.

    :param plan_optimization:

    :return: true for successful execution, false for failure

    :todo:  -   identify oars by organ type to avoid accidentally using an incorrect type in
                reduce oars
            -   if Raystation support composite optimization + ReduceOARDose at some point
                the conditional should be removed
    """
    #
    # targets to be identified by their organ type and oars are assigned to everything else
    targets = []
    oars = []
    # Figure out if this plan is co-optimized and reject any ReduceOAR if it is
    # Do this by searching the objective functions for those that have a beamset
    # attribute
    composite_objectives = []
    for index, objective in enumerate(
            plan_optimization.Objective.ConstituentFunctions):
        if hasattr(objective.OfDoseDistribution, 'ForBeamSet'):
            composite_objectives.append(index)
    # If composite objectives are found warn the user
    if composite_objectives:
        connect.await_user_input(
            "ReduceOAR with composite optimization is not supported " +
            "by RaySearch at this time")
        logging.warning(
            "automated_plan_optimization.py: reduce_oar_dose: " +
            "RunReduceOARDoseOptimization not executed due to the presence of"
            + "CompositeDose objectives")
        logging.debug(
            "automated_plan_optimization.py: reduce_oar_dose: composite" +
            "objectives found in iterations {}".format(composite_objectives))
        return False
    else:
        # Construct the currently-used targets and regions at risk as lists targets and oars
        # respectively
        logging.info(
            "automated_plan_optimization.py: reduce_oar_dose: no composite" +
            "objectives found, proceeding with ReduceOARDose")
        for objective in plan_optimization.Objective.ConstituentFunctions:
            objective_organ_type = objective.OfDoseGridRoi.OfRoiGeometry.OfRoi.OrganData.OrganType
            objective_roi_name = objective.OfDoseGridRoi.OfRoiGeometry.OfRoi.Name
            if objective_organ_type == 'Target':
                objective_is_target = True
            else:
                objective_is_target = False
            if objective_is_target:
                # Add only unique elements to targets
                if objective_roi_name not in targets:
                    targets.append(objective_roi_name)
            else:
                # Add only unique elements to oars
                if objective_roi_name not in oars:
                    oars.append(objective_roi_name)
        sorted_structure_message = "automated_plan_optimization.py: reduce_oar_dose: " + \
                                   "Reduce OAR dose executing with targets: " + ', '.join(targets)
        sorted_structure_message += " and oars: " + ', '.join(oars)
        logging.info(sorted_structure_message)

        try:
            test_success = plan_optimization.RunReduceOARDoseOptimization(
                UseVoxelBasedMimickingForTargets=False,
                UseVoxelBasedMimickingForOrgansAtRisk=False,
                OrgansAtRiskToImprove=oars,
                TargetsToMaintain=targets,
                OrgansAtRiskToMaintain=oars)
            print test_success
            return True
        except:
            return False
def main():
    # Script will, determine the current machine set for which this beamset is planned
    # Submit the first plan via export method
    # Copy the beamset using the RS CopyAndAdjust method
    # Recompute dose on Copied plan.
    # TODO: Determine the number of beamsets belonging to this plan, and if they are approved, send all

    # Get current patient, case, exam, plan, and beamset
    try:
        patient = connect.get_current('Patient')
        case = connect.get_current('Case')
        exam = connect.get_current('Examination')

    except Exception:
        UserInterface.WarningBox('This script requires a patient to be loaded')
        sys.exit('This script requires a patient to be loaded')

    try:
        parent_plan = connect.get_current('Plan')
        parent_beamset = connect.get_current('BeamSet')

    except Exception:
        logging.debug(
            'A plan and/or beamset is not loaded; plan export options will be disabled'
        )
        parent_plan = None
        parent_beamset = None

    if 'Tomo' in parent_beamset.DeliveryTechnique:
        success = True

        logging.debug('Status of sending parent plan: {}'.format(success))
        machine_1 = 'HDA0488'
        machine_2 = 'HDA0477'
        if machine_1 in parent_beamset.MachineReference['MachineName']:
            parent_machine = machine_1
            daughter_machine = machine_2
        elif machine_2 in parent_beamset.MachineReference['MachineName']:
            parent_machine = machine_2
            daughter_machine = machine_1
        else:
            logging.error('Unknown Tomo System, Aborting')
            sys.exit('Unknown Tomo System: {} Aborting'.format(
                parent_beamset.MachineReference['MachineName']))

        case.CopyAndAdjustTomoPlanToNewMachine(PlanName=parent_plan.Name,
                                               NewMachineName=daughter_machine,
                                               OnlyCopyAndChangeMachine=False)
        patient.Save()

        parent_beamset_name = parent_beamset.DicomPlanLabel
        daughter_plan_name = parent_plan.Name + '_Transferred'
        daughter_plan = case.TreatmentPlans[daughter_plan_name]

        daughter_beamset = PlanOperations.find_beamset(
            plan=daughter_plan, beamset_name=parent_plan.Name[:8], exact=False)
        if daughter_beamset is None:
            logging.error('No daughter beamset {} found in {}, exiting'.format(
                parent_beamset_name, daughter_plan.Name))
            sys.exit('Could not find transferred beamset for export')

        daughter_plan.SetCurrent()
        connect.get_current('Plan')
        daughter_beamset.SetCurrent()
        connect.get_current('BeamSet')
        daughter_beamset.ComputeDose(ComputeBeamDoses=True,
                                     DoseAlgorithm="CCDose",
                                     ForceRecompute=False)
        daughter_plan.Name = parent_plan.Name[:8] + '_Tr'
        daughter_beamset.DicomPlanLabel = parent_beamset_name[:
                                                              8] + '_Tr' + daughter_machine[
                                                                  -3:]
        # Cannot set DSP's once the plan is created or a DICOM failure occurs
        # BeamOperations.set_dsp(plan=daughter_plan, beam_set=daughter_beamset)

        patient.Save()
        # Plans must be approved in iDMS prior to sending the transfer plan.
        connect.await_user_input(
            'Please go to the iDMS workstation and approve plan {}, '.format(
                parent_plan.Name) + 'Then continue script')
        parent_plan_iDMS_name = parent_plan.Name + ':' + parent_beamset_name
        # daughter_beamset.SendTransferredPlanToRayGateway(RayGatewayTitle='RAYGATEWAY',PreviousBeamSet=parent_plan_iDMS_name,OriginalBeamSet=parent_plan_iDMS_name,IgnorePreConditionWarnings=True)

        success = DicomExport.send(case=case,
                                   destination='RayGateway',
                                   parent_plan=parent_plan_iDMS_name,
                                   exam=exam,
                                   beamset=daughter_beamset,
                                   ct=True,
                                   structures=True,
                                   plan=True,
                                   plan_dose=True,
                                   beam_dose=False,
                                   ignore_warnings=True,
                                   ignore_errors=False,
                                   rename=None,
                                   filters=None,
                                   machine=None,
                                   table=None,
                                   round_jaws=False,
                                   prescription=False,
                                   block_accessory=False,
                                   block_tray_id=False,
                                   bar=True)
import connect
import logging
import UserInterface
import random
import sys


def check_structure_exists(case, structure_name, roi_list, option):
    if any(roi.OfRoi.Name == structure_name for roi in roi_list):
        if option == 'Delete':
            case.PatientModel.RegionsOfInterest[structure_name].DeleteRoi()
            logging.warning("autoplan_whole_brain.py: check_structure_exists: " +
                            structure_name + 'found - deleting and creating')
        elif option == 'Check':
            connect.await_user_input(
                'Contour {} Exists - Verify its accuracy and continue script'.format(structure_name))
        return True
    else:
        logging.info('autoplan_whole_brain.py: check_structure_exists: '
                     'Structure {} not found, and will be created'.format(structure_name))
        return False


def main():
    # Script will run through the following steps.  We have a logical inconsistency here with making a plan
    # this is likely an optional step
    status = UserInterface.ScriptStatus(
        steps=['SimFiducials point declaration',
               'Making the target',
               'Verify PTV_WB_xxxx coverage',
               'User Inputs Plan Information',
Beispiel #22
0
def main():
    """
    Function will take the optional input of the protocol file name
    :return:
    """
    filename = None
    status = UserInterface.ScriptStatus(steps=[
        'Finding correct protocol', 'Matching Structure List',
        'Getting target Doses', 'Adding Goals', 'Adding Standard Objectives'
    ],
                                        docstring=__doc__,
                                        help=__help__)

    protocol_folder = r'../protocols'
    institution_folder = r'UW'
    path_protocols = os.path.join(os.path.dirname(__file__), protocol_folder,
                                  institution_folder)

    # Get current patient, case, exam, and plan
    patient = find_scope(level='Patient')
    case = find_scope(level='Case')
    exam = find_scope(level='Examination')
    plan = find_scope(level='Plan')
    beamset = find_scope(level='BeamSet')

    tpo = UserInterface.TpoDialog()
    tpo.load_protocols(path_protocols)

    status.next_step(text="Determining correct treatment protocol" +
                     "based on treatment planning order.",
                     num=0)

    # TODO: Set up a means of bypassing the dialogs
    #  Eventually we may want to convert to accepting a call from a filename
    #  Alternatively, this could all be set up as a function call
    if filename:
        logcrit("Protocol selected: {}".format(filename))
        root = tpo.protocols[tpo.protocols[filename]]
    else:
        # Find the protocol the user wants to use.
        input_dialog = UserInterface.InputDialog(
            inputs={'i': 'Select Protocol'},
            title='Protocol Selection',
            datatype={'i': 'combo'},
            initial={},
            options={'i': list(tpo.protocols.keys())},
            required=['i'])
        # Launch the dialog
        response = input_dialog.show()
        # Link root to selected protocol ElementTree
        logging.info("Protocol selected: {}".format(input_dialog.values['i']))
        # Store the protocol name and optional order name
        protocol_name = input_dialog.values['i']
        order_name = None
        order_list = []
        protocol = tpo.protocols[input_dialog.values['i']]
        for o in protocol.findall('order/name'):
            order_list.append(o.text)

        if len(order_list) >= 1:
            use_orders = True
            # Find the protocol the user wants to use.
            input_dialog = UserInterface.InputDialog(
                inputs={'i': 'Select Order'},
                title='Order Selection',
                datatype={'i': 'combo'},
                initial={'i': order_list[0]},
                options={'i': order_list},
                required=['i'])
            # Launch the dialog
            response = input_dialog.show()
            # Link root to selected protocol ElementTree
            logging.critical("Order selected: {}".format(
                input_dialog.values['i']))
            # Update the order name

            # I believe this loop can be eliminated with we can use a different function
            # to match protocol.find('order') with input_dialog.values['i']
            for o in protocol.findall('order'):
                if o.find('name').text == input_dialog.values['i']:
                    order = o
                    logging.debug(
                        'Matching protocol ElementTag found for {}'.format(
                            input_dialog.values['i']))
                    break
            order_name = input_dialog.values['i']

        else:
            logging.debug('No orders in protocol')
            use_orders = False

        # Match the list of structures found in the objective protocols and protocols

        # Find RS targets
        plan_targets = StructureOperations.find_targets(case=case)
        status.next_step(text="Matching all structures to the current list.",
                         num=1)

        protocol_targets = []
        missing_contours = []

        # Build second dialog
        target_inputs = {}
        target_initial = {}
        target_options = {}
        target_datatype = {}
        target_required = []
        i = 1
        # Lovely code, but had to break this loop up
        # for g, t in ((a, b) for a in root.findall('./goals/roi') for b in plan_targets):

        if use_orders:
            goal_locations = (protocol.findall('./goals/roi'),
                              order.findall('./goals/roi'))
        else:
            goal_locations = (protocol.findall('./goals/roi'))
        # Use the following loop to find the targets in protocol matching the names above
        for s in goal_locations:
            for g in s:
                g_name = g.find('name').text
                # Priorities should be even for targets and append unique elements only
                # into the protocol_targets list
                if int(g.find('priority').text
                       ) % 2 == 0 and g_name not in protocol_targets:
                    protocol_targets.append(g_name)
                    k = str(i)
                    # Python doesn't sort lists....
                    k_name = k.zfill(2) + 'Aname_' + g_name
                    k_dose = k.zfill(2) + 'Bdose_' + g_name
                    target_inputs[k_name] = 'Match a plan target to ' + g_name
                    target_options[k_name] = plan_targets
                    target_datatype[k_name] = 'combo'
                    target_required.append(k_name)
                    target_inputs[
                        k_dose] = 'Provide dose for protocol target: ' + g_name + ' Dose in cGy'
                    target_required.append(k_dose)
                    i += 1
                    # Exact matches get an initial guess in the dropdown
                    for t in plan_targets:
                        if g_name == t:
                            target_initial[k_name] = t

        # Warn the user they are missing organs at risk specified in the order
        rois = []  # List of contours in plan
        protocol_rois = [
        ]  # List of all the regions of interest specified in the protocol

        for r in case.PatientModel.RegionsOfInterest:
            # Maybe extend, can't remember
            rois.append(r.Name)
        for s in goal_locations:
            for g in s:
                g_name = g.find('name').text
                if g_name not in protocol_rois: protocol_rois.append(g_name)
                # Add a quick check if the contour exists in RS
                # This step is slow, we may want to gather all rois into a list and look for it
                if int(g.find('priority').text) % 2:
                    if not any(
                            r == g_name
                            for r in rois) and g_name not in missing_contours:
                        #       case.PatientModel.RegionsOfInterest) and g_name not in missing_contours:
                        missing_contours.append(g_name)

        # Launch the matching script here. Then check for any missing that remain. Supply function with rois and
        # protocol_rois

        if missing_contours:
            mc_list = ',\n'.join(missing_contours)
            missing_message = 'Missing structures, continue script or cancel \n' + mc_list
            status.next_step(text=missing_message, num=1)
            connect.await_user_input(missing_message)
            # Add a line here to check again for missing contours and write out the list
            for r in case.PatientModel.RegionsOfInterest:
                # Maybe extend, can't remember
                rois.append(r.Name)

            m_c = []
            found = False
            for m in missing_contours:
                # We don't want in, we need an exact match - for length too
                for r in rois:
                    found = False
                    if r == m:
                        found = True
                        break
                if not found:
                    if m not in m_c:
                        m_c.append(m)
            if not m_c:
                logging.debug('All structures in protocol accounted for')
            else:
                mc_list = ',\n'.join(m_c)
                missing_message = 'Missing structures remain: ' + mc_list
                logging.warning(
                    'Missing contours from this order: {}'.format(m_c))
Beispiel #23
0
    # Prompt user to approve structures
    logging.debug('Prompting user to approve structure set')
    status.next_step(
        text=
        'You will now be prompted to approve the structure set. Once completed, continue the script.'
    )

    if changes > 0:
        ui = connect.get_current('ui')
        ui.TitleBar.MenuItem['Patient Modeling'].Click()
        ui.TitleBar.MenuItem['Patient Modeling'].Popup.MenuItem[
            'Structure Definition'].Click()
        ui.TabControl_ToolBar.Approval.Select()
        ui.ToolPanel.TabItem._Scripting.Select()
        connect.await_user_input(
            'Approve the structure set now, then continue the script')

    # Create new plan (ICDa-z Protocol)
    status.next_step(
        text=
        'A new plan will now be created and populated with the TPO template clinical goals...'
    )
    patient.Save()

    for a in range(1, 26):
        plan_name = '{}{} {}'.format(response['diagnosis'][0],
                                     chr(64 + a).lower(), response['order'])
        try:
            logging.debug('Plan name already exists: ' +
                          case.TreatmentPlans[plan_name].Name)
def main():
    # Script will run through the following steps.  We have a logical inconsistency here with making a plan
    # this is likely an optional step
    status = UserInterface.ScriptStatus(
        steps=['SimFiducials point declaration',
               'Making the target',
               'Verify PTV_WB_xxxx coverage',
               'User Inputs Plan Information',
               'Regions at Risk Generation/Validation',
               'Support Structure Loading',
               'Target (BTV) Generation',
               'Plan Generation (Optional)'],
        docstring=__doc__,
        help=__help__)

    # UW Inputs
    # If machines names change they need to be modified here:
    institution_inputs_machine_name = ['TrueBeamSTx', 'TrueBeam']
    # The s-frame object currently belongs to an examination on rando named: "CT 1"
    # if that changes the s-frame load will fail
    institution_inputs_support_structures_examination = "CT 1"
    institution_inputs_support_structure_template = "UW Support Structures"
    institution_inputs_source_roi_names = ['S-frame']
    try:
        patient = connect.get_current('Patient')
        case = connect.get_current("Case")
        examination = connect.get_current("Examination")
        patient_db = connect.get_current('PatientDB')
    except:
        UserInterface.WarningBox('This script requires a patient, case, and exam to be loaded')
        sys.exit('This script requires a patient, case, and exam to be loaded')

    # Capture the current list of ROI's to avoid saving over them in the future
    rois = case.PatientModel.StructureSets[examination.Name].RoiGeometries

    # Capture the current list of POI's to avoid a crash
    pois = case.PatientModel.PointsOfInterest

    visible_structures = ['PTV_WB_xxxx', 'Lens_L', 'Lens_R']
    invisible_stuctures = [
        'Globe_L',
        'Globe_R',
        'External',
        'S-frame',
        'Avoid',
        'Avoid_Face',
        'Lens_R_PRV05',
        'Lens_L_PRV05',
        'BTV_Brain',
        'BTV_Flash_20',
        'BTV',
        'Brain']
    # Look for the sim point, if not create a point
    sim_point_found = any(poi.Name == 'SimFiducials' for poi in pois)
    if sim_point_found:
        logging.warning("autoplan_whole_brain.py: POI SimFiducials Exists")
        status.next_step(text="SimFiducials Point found, ensure that it is placed properly")
        connect.await_user_input('Ensure Correct placement of the SimFiducials Point and continue script.')
    else:
        case.PatientModel.CreatePoi(Examination=examination,
                                    Point={'x': 0,
                                           'y': 0,
                                           'z': 0},
                                    Volume=0,
                                    Name="SimFiducials",
                                    Color="Green",
                                    Type="LocalizationPoint")
        status.next_step(text="SimFiducials POI created, ensure that it is placed properly")
        connect.await_user_input('Ensure Correct placement of the SimFiducials Point and continue script.')