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]
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
'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
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
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")
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(
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.
# 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. ' +
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')
.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(
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:
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',
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))
# 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.')