public static void csvWritePhase(DataStruc Result, StreamWriter myWriterPhase, string filename) { for (int i = 0; i < Result.key.Count(); i++) { myWriterPhase.Write( Result.ID + "," + Result.course + "," + Result.plan + "," + Result.protocol + "," + Result.PD + "," + Result.plannedOn + "," + Result.SSiD + "," + Result.fourD + "," + Result.phase[i] + "," + Result.GTVPhVol[i] + "," + Result.xGTVph[i] / 10 + "," + Result.yGTVph[i] / 10 + "," + Result.zGTVph[i] / 10 + "," + Result.xGTVph2Lung[i] / 10 + "," + Result.yGTVph2Lung[i] / 10 + "," + Result.zGTVph2Lung[i] / 10 + "," + Result.xGTVph2PTV[i] / 10 + "," + Result.yGTVph2PTV[i] / 10 + "," + Result.zGTVph2PTV[i] / 10 + "," + Result.PTVVol + "," + Result.LungVol + "," + Result.key[i]); myWriterPhase.Write("\n"); } }
public static void csvWritePlan(DataStruc Result, StreamWriter myWriterPlan, string filename) { //Summarises the 4D information per plan into 1 line. string output = Result.planKey + "," + Result.ID + "," + Result.course + "," + Result.Site + "," + Result.plan + "," + Result.protocol + "," + Result.PD + "," + Result.plannedOn + "," + Result.SSiD + "," + Result.PTVVol + "," + Result.LungVol + "," + Result.fourD; //Only includes 4D data if it was found. if (Result.GTVPhVol.Count() != 0) { //Determines laterality from tumour co-ordinates. string Laterality = "Unknown"; if (Result.xGTVph2Lung.Average() < -20 && Result.zGTVph2Lung.Average() < 0) { Laterality = "Left Upper"; } else if (Result.xGTVph2Lung.Average() < -20 && Result.zGTVph2Lung.Average() >= 0) { Laterality = "Left Lower"; } else if (Result.xGTVph2Lung.Average() > 20 && Result.zGTVph2Lung.Average() < 0) { Laterality = "Right Upper"; } else if (Result.xGTVph2Lung.Average() > 20 && Result.zGTVph2Lung.Average() >= 0) { Laterality = "Right Lower"; } else { if (Result.zGTVph2Lung.Average() < 0) { Laterality = "Central Upper"; } else if (Result.zGTVph2Lung.Average() > 0) { Laterality = "Central Lower"; } } output = output + "," + Result.GTVPhVol.Average() + "," + Result.xGTVph.Max() / 10 + "," + Result.yGTVph.Max() / 10 + "," + Result.zGTVph.Max() / 10 + "," + Result.xGTVph2Lung.Average() / 10 + "," + Result.yGTVph2Lung.Average() / 10 + "," + Result.zGTVph2Lung.Average() / 10 + "," + Result.xGTVph2PTV.Average() / 10 + "," + Result.yGTVph2PTV.Average() / 10 + "," + Result.zGTVph2PTV.Average() / 10 + "," + Laterality; } //Outputs string. myWriterPlan.Write(output); myWriterPlan.Write("\n"); }
static void Execute(Application app) { //Intro Console.WriteLine("Standalone script to pull imaging data from 4DCT planning scan structure sets"); //Open input text file for list of ID numbers. StreamReader myReader = new StreamReader(getFilePath(@"\\oncology-is\VA_DATA$\physicists\ESAPI Scripting\Patient lists")); System.Collections.ArrayList IDList = new System.Collections.ArrayList(); //empty array //Reads in ID number and assigns to array. string IDNo = myReader.ReadLine(); while (IDNo != null) { IDList.Add(IDNo); IDNo = myReader.ReadLine(); } //set output file location string outputDir = @"\\oncology-is\VA_DATA$\physicists\ESAPI Scripting\Reports"; string filename = string.Format(@"{0}\LungMotionPhase_{1}.csv", outputDir, DateTime.Now.ToString("dd_MM_yyyy")); Console.WriteLine("Output location for Phase info:"); Console.WriteLine(filename); string filename2 = string.Format(@"{0}\LungMotionPlan_{1}.csv", outputDir, DateTime.Now.ToString("dd_MM_yyyy")); Console.WriteLine("Output location for Plan info:"); Console.WriteLine(filename2); //Creates file for phase information System.IO.StreamWriter myWriterPhase = new StreamWriter(filename); //creates empty .csv myWriterPhase.AutoFlush = true; //Writes header for csv for phases myWriterPhase.Write("ID, Course, Plan, Protocol, Dose, Plan creation, Structure Set, 4D?, Phase, GTVvolume (cc)," + "x-lat (cm),y-vrt (cm),z-lng (cm), " + "x-lat2Lung (cm),y-vrt2Lung (cm),z-lng2Lung (cm), " + "x-lat2PTV (cm),y-vrt2PTV (cm),z-lng2PTV (cm), " + "PTV Vol(cc), lungVol(cc), key"); myWriterPhase.Write("\n"); //Creates file for summary information for each plan. System.IO.StreamWriter myWriterPlan = new StreamWriter(filename2); //creates empty .csv myWriterPhase.AutoFlush = true; //Writes header for csv for plan. myWriterPlan.Write("Key, ID, Course, Site, Plan, Protocol, Dose, Plan creation, Structure Set, " + "PTV Vol (cc), lungVol (cc), 4D?, GTV mean vol (cc), " + "x-lat range (cm),y-vrt range (cm), z-lng range (cm), " + "x-lat2Lung (cm), y-vrt2Lung (cm), z-lng2Lung (cm), " + "x-lat2PTV (cm), y-vrt2PTV (cm), z-lng2PTV (cm), Laterality,"); myWriterPlan.Write("\n"); double Count = 0; //counts patients. foreach (string id in IDList) //runs through list of IDs { Count++; Console.WriteLine("Processing patient {0} of {1}, id: {2}", Count, IDList.Count, id); Patient pat = app.OpenPatientById(id); foreach (var course in pat.Courses) //Runs through all courses { //filters for relevant course if (course.Id.ToUpper().Contains("QA") || course.Id.ToUpper().Contains("QC") || course.Id.ToUpper().Contains("XIO") || !course.Id.ToUpper().Contains("LUNG")) { //skips irrelevant courses for loop continue; } foreach (var ps in course.PlanSetups) //Runs through all plans { // Checks for arcs. int arcs = 0; for (int Beams = 0; Beams < ps.Beams.Count(); Beams++) { if ((ps.Beams.ElementAt(Beams).IsSetupField)) { continue; } if (ps.Beams.ElementAt(Beams).Technique.Id.ToUpper().Contains("ARC")) { arcs++; } //adds to index if criteria are met. } string Approval = ps.ApprovalStatus.ToString(); //Filters plans if (!(ps.IsDoseValid) // skip plans with no dose // || Approval.Contains("Retired") // skips retired || Approval.Contains("Rejected") // skips rejected // || !(Approval.Contains("TreatmentApproved")) // only includes treatment approved plans || ps.Id.ToUpper().Contains("QA") || // skip QA plans ps.Id.ToUpper().Contains("SCRIPT") || // skip plans with script in ID ps.Id.ToUpper().Contains("#") //|| !ps.Id.ToUpper().Contains("SABR") || ps.StructureSet == null || // no structure set arcs == 0 // no arcs ) { continue; } if (ps.IsTreated) { //placed in a try catch loop to catch errors but not end code. try { //Passes data to method to work out results. DataStruc tempData = processPlan(pat, ps); //Writes phases information to csv csvWritePhase(tempData, myWriterPhase, filename); //Writes plan information to csv csvWritePlan(tempData, myWriterPlan, filename2); } catch (Exception e) { //outputs error to console. Console.Error.WriteLine(e.ToString()); } } } } app.ClosePatient(); } myWriterPhase.Close(); myWriterPlan.Close(); Console.WriteLine("All patients processed. Hit Return to open results and exit console"); Console.ReadLine(); System.Diagnostics.Process.Start(filename); //opens results spreadsheet System.Diagnostics.Process.Start(filename2); //opens results spreadsheet }
public static DataStruc processPlan(Patient pat, PlanSetup ps) { //Sets the image FOR which is unique to images acquired at the same time. string FOR = ps.StructureSet.Image.FOR; StructureSet mainSS = ps.StructureSet; //Makes place holder for results. DataStruc Result = new DataStruc(); //Produces generic plan results. Result.ID = pat.Id; Result.SSiD = mainSS.Id; Result.PD = ps.TotalDose.Dose; Result.protocol = ps.ProtocolID; Result.plan = ps.Id; Result.plannedOn = ps.HistoryDateTime; Result.Site = SiteFind(ps.ProtocolID, ps.Course.Id); Result.course = ps.Course.Id; Result.planKey = ps.UID + ps.Id; Result.fourD = "No"; //Pre-creates list to assign phase data to. Result.GTVPhVol = new List <double>(); Result.phase = new List <string>(); Result.xGTVph = new List <double>(); Result.yGTVph = new List <double>(); Result.zGTVph = new List <double>(); Result.xGTVph2Lung = new List <double>(); Result.yGTVph2Lung = new List <double>(); Result.zGTVph2Lung = new List <double>(); Result.xGTVph2PTV = new List <double>(); Result.yGTVph2PTV = new List <double>(); Result.zGTVph2PTV = new List <double>(); Result.key = new List <string>(); //Creates relevant structures using linq. Structure Lungs = null; Lungs = mainSS.Structures.Where(x => x.Id.ToUpper() == "LUNGS").FirstOrDefault(); if (Lungs == null) { Lungs = mainSS.Structures.Where(x => x.Id.ToUpper() == "WHOLE LUNG").FirstOrDefault(); } if (Lungs == null) { Lungs = mainSS.Structures.Where(x => x.Id.ToUpper() == "LUNGS-GTV").FirstOrDefault(); } if (Lungs != null) { Result.LungVol = Lungs.Volume; } else { Result.LungVol = 0; } Structure PTV = null; PTV = mainSS.Structures.Where(x => x.Id.ToUpper() == "PTV").FirstOrDefault(); if (PTV == null) { PTV = mainSS.Structures.Where(x => x.Id.ToUpper().Contains("PTV")).FirstOrDefault(); } Result.PTVVol = PTV.Volume; //Makes a collection of 4DCT phases, not including CBCT. var SSColl = pat.StructureSets.Where(x => x.Image.FOR == FOR && !x.Id.ToUpper().Contains("CBCT") && !x.Id.ToUpper().Contains("INTERPLAY") && x.Id.ToUpper().Contains("CT")); if (SSColl != null) { //Cycle through phases to find CoM and volumes. foreach (var SS in SSColl) { //Cycles through structures in structureset. foreach (var S in SS.Structures) { if (S.Id.ToUpper().Equals("GTV PH") && S.IsEmpty != true) // finds the centre of the GTV { //Commit phase ranges to list. Result.xGTVph.Add(S.CenterPoint.x); Result.yGTVph.Add(S.CenterPoint.y); Result.zGTVph.Add(S.CenterPoint.z); Result.GTVPhVol.Add(S.Volume); Result.phase.Add(SS.Id); //Adds to primary key if phases are present. Result.key.Add(ps.UID + SS.Id); //Determines offset between PTV and phase. +1000 removes the impact of sign changes. Result.xGTVph2PTV.Add((1000 + PTV.CenterPoint.x) - (1000 + S.CenterPoint.x)); Result.yGTVph2PTV.Add((1000 + PTV.CenterPoint.y) - (1000 + S.CenterPoint.y)); Result.zGTVph2PTV.Add((1000 + PTV.CenterPoint.z) - (1000 + S.CenterPoint.z)); if (Lungs != null) { Result.xGTVph2Lung.Add((1000 + Lungs.CenterPoint.x) - (1000 + S.CenterPoint.x)); Result.yGTVph2Lung.Add((1000 + Lungs.CenterPoint.y) - (1000 + S.CenterPoint.y)); Result.zGTVph2Lung.Add((1000 + Lungs.CenterPoint.z) - (1000 + S.CenterPoint.z)); } else { Result.xGTVph2Lung.Add(0); Result.yGTVph2Lung.Add(0); Result.zGTVph2Lung.Add(0); } } } } //Reduces the GTV phase co-ordinate to be relative to zero. if (Result.xGTVph.Count >= 1) { Result.fourD = "Yes"; double minX = Result.xGTVph.Min(); double minY = Result.yGTVph.Min(); double minZ = Result.zGTVph.Min(); for (int i = 0; i < Result.key.Count(); i++) { Result.xGTVph[i] = (1000 + Result.xGTVph[i]) - (1000 + minX); //1000 + removes problem of going over the axis, result is relative anyway. Result.yGTVph[i] = (1000 + Result.yGTVph[i]) - (1000 + minY); Result.zGTVph[i] = (1000 + Result.zGTVph[i]) - (1000 + minZ); } } } else { Result.key.Add(ps.UID + ps.Id); } return(Result); //returns result. }