I'm used to programming in C# when creating Eclipse scripts with ESAPI. But there's a lot of ceremony in that—opening Visual Studio, creating a new solution and project, referencing the ESAPI assemblies, creating a new program class, etc. That's all fine if I'm creating a script that will be used by others.
But sometimes I just want to quickly get some information about a patient or patients and be done. It turns out that one can use Python to do this. Python is a general-purpose scripting language that's easy to learn and use. Python was added to the family of .NET languages as IronPython. This is good because, as you may know, ESAPI is based on the .NET framework and therefore requires a .NET language to use it.
To get started, download and install IronPython. You can also install additional tools to integrate Python into Visual Studio, but I haven't tried that yet. I'm just going to use a plain text editor.
Here's the basic template of an ESAPI script in Python:
import sys import clr import System # Add Varian's path when referencing assemblies sys.path.append(r"C:\Program Files (x86)\Varian\Vision\13.6\Bin64") # Reference the Varian API assemblies clr.AddReferenceToFile("VMS.TPS.Common.Model.API.dll") clr.AddReferenceToFile("VMS.TPS.Common.Model.Types.dll") from VMS.TPS.Common.Model.API import * from VMS.TPS.Common.Model.Types import * app = Application.CreateApplication("username", "password") # Your code here app.Dispose()
First, you tell it where the ESAPI assemblies are, and then you reference the required assemblies. You then create the application object (passing it your username and password). After your code, you dispose of the application object. (If you don't dispose of it, you'll get an annoying error before the program exists.)
Let's use this template in an example. I'd like to output the multileaf collimator (MLC) leaf positions of all control points in a beam for a patient. Each output line will contain the control point index, the control point's meterset weight, the jaw positions, and the positions of a leaf pair. Here's the code:
patient = app.OpenPatientById("1234567890") course = next(c for c in patient.Courses if c.Id == "COURSE") plan = next(p for p in course.PlanSetups if p.Id == "PLAN") beam = next(b for b in plan.Beams if b.Id == "BEAM") for cpIndex in range(beam.ControlPoints.Count): cp = beam.ControlPoints[cpIndex] for leafPairIx in range(cp.LeafPositions.GetLength(1)): print cpIndex + 1, \ cp.MetersetWeight, \ cp.JawPositions.X1, \ cp.JawPositions.X2, \ cp.JawPositions.Y1, \ cp.JawPositions.Y2, \ cp.LeafPositions[0, leafPairIndex], \ cp.LeafPositions[1, leafPairIndex] app.ClosePatient()
As another example, I'd like to output the plan sums that contain plans with different structure set images. Here's the code:
for ps in app.PatientSummaries: try: patient = app.OpenPatientById(ps.Id) if patient.Courses == None: continue for course in patient.Courses: if course.PlanSums == None: continue for pls in course.PlanSums: if pls.PlanSetups == None: continue plans = list(pls.PlanSetups) if len(plans) < 2: continue if plans.StructureSet == None or \ plans.StructureSet == None: continue if plans.StructureSet.Image.FOR != \ plans.StructureSet.Image.FOR: print patient.Id, course.Id, pls.Id except: print "Error with patient " + ps.Id finally: app.ClosePatient()
Each of these examples were coded in a single file—no need for solutions, projects, classes, etc. The code itself is also fairly easy to read because the language is streamlined and devoid of unnecessary syntax.
The IronPython Documentation website contains several useful links, including the Python language documentation and a reference on how IronPython integrates with the .NET framework.