top of page
  • Black LinkedIn Icon
  • YouTube
  • Gateway Scripts
  • Whatsapp

Script Approval Extensions - V18 scripting first look

Every major release of ESAPI opens new doors for clinical workflow improvement — Version 18.1 of Eclipse is no different. For the first time, we now have the ability to participate directly in the plan approval event inside Eclipse. The new Script Approval Extensions introduce a reliable, centralized enforcement point that can support clinical safety, documentation, and even automation in a way that wasn’t possible before.


Script Approval extensions allow developers to ensure that script execution is performed at a specific time in the workflow -- during the plan approval. This ensures that all dosimetric quality of the treatment plan has already been reviewed and approved by the clinician team, allowing the developer to have confidence that certain pieces to the planning process are already in place (e.g. the prescription is linked to the plan, the clinical goals have been defined, etc.).

ree

Approval extensions have already generated a lot of excitement within our group because they allow us to codify institutional standards directly into the approval workflow, without depending on tasks, checklists, or physics checking experience. Some of the extensions we plan to roll out include:

  • TG-263 Structure Name Checker: Alerts the approver if structure names do not conform to AAPM Task Group 263 conventions — eliminating downstream confusion and improving data standardization.

  • Automated Beam Naming Enforcement: Automatically populates standard beam names if the field names are missing, or validates user-entered names if present. This helps maintain consistent naming in downstream plan reporting and QA analytics.

  • Automated Setup Field Creation: Automatically inserts setup fields for our TrueBeam machines when missing, ensuring a complete and standardized beam configuration while freeing planners to focus on plan quality rather than clerical data entry.

These extensions establish a foundation for safer, more efficient, and more standardized clinical planning across our fleet.


Script Approval Extension Requirements

In the event any readers are returning to this blog post to ensure all the script approval extension steps have been followed, it is beneficial to have them at the top of the blog post. Please see a concise list of required steps for publishing the Script Approval Extension -- a detailed description with screenshots will be provided in context within the blog post's example script.


Building the Extension Plugin:

  1. Open the Eclipse Script Wizard. Select the Script Approval Extension script type.

  2. See the programming example below for code examples to add to the template.


Before Compiling Extension Plugin: 

  1. In Visual Studio, go to Project --> Properties. Go to the Signing tab on the left. Check the box that says Sign the Assembly.

  2. In the dropdown select new. Give the key file a name (i.e. ApprovalExtensionKey.snk).

  3. Ensure the AssemblyVersion is unique by adding a number to the assembly version.

  4. Build the script.


Installing Script Approval Extension:

  1. Go to Eclipse. Click Tools--> Scripts, click on System Scripts Radio button and click Open Folder to open the System Scripts location.

    1. Create a folder path called Extensions--> Script name up to the ".dll" --> Assembly Version (i.e. Extensions\BeamNamerApprExt.approvalextension\1.0.0.1\).

    2. Copy/paste your approvalextension.dll from Plugins folder into the new version folder in your published scripts folder

  2. Go to Tools--> Script Administration. Locate your scripts in the proper folder and approve the script or approve the script for evaluation testing.

  3. Go to Quicklinks--> Administration--> RT Administration. Then go to Tools--> Approval Extensions. First, remove any old versions of your approval extension, then click add and add your latest version of the approval extension. After adding your latest approval extension click OK.

  4. Return to External Beam Planning and try to approve a plan. 


Generating a Script Approval Extension Script


The final version of the plan checking approval extension script can be found in the ApprovalChecks_ApprExt github repository for cloning or download to follow along with the example.


What are Script Approval Extensions?

An Approval Extension is a binary plug-in script that participates in the Plan Approval Wizard in Eclipse and BrachyVision. Unlike a typical ESAPI script, these plug-ins are not executed manually by a user—they run automatically whenever a plan is promoted to Approved or Reviewed.

Approval extensions can:

Capability

Example

Produce info/warning/error messages

Alerting users that a certain protocol rule wasn't met

Block approval if an error is returned

Similar to traditional Eclipse plan validation failures

Show a modal dialog to the user

Manual acknowledgment, custom checklists, instructions

Receive the wizard’s output messages afterwards

Logging, audits, downstream automation

Perform automations

Modifications to plan and beam parameters can be implemented during approval. Note: No dosimetric changes may be made to the plan during a script approval extension.

Approval extensions execute twice during the approval process:

  1. Before the approval wizard opens

This enables custom logic to validate plan characteristics and optionally return:

  • Information messages

  • Warnings

  • Errors—blocking approval

public PlanValidationResult PrePromote(
    PlanSetupApprovalStatus newStatus,
    IEnumerable<PlanSetup> plans)

The return value is a PlanValidationResult, and returning null means there are no results to report.

  1. Immediately after the plan is approved

At this point, the plan has already been committed to the database.

public void PostPromote(
    PlanSetupApprovalStatus newStatus,
    IEnumerable<PlanSetup> plans,
    PlanValidationResult results)

The results parameter contains the warnings/information messages that the approval wizard presented to the user.


Developing the Script

To begin, open the Eclipse Script Wizard and select the Approval extension script type. For this example, we can call the Script ApprovalChecks.

ree

Within the approval extension script, there are a set of templated methods that are described in the section above. In this example below, we will use the PrePromote method to do some plan checks.

  1. One plan check that ensures the dose calculation algorithm utilizes a heterogeneity correction. If this check fails, the approval window results in a warning.

  2. A second plan check detects whether a prescription is associated to the plan matches the plan's prescription. In this instance if the check fails, the extension will produce an error to the plan approval blocking the approval from moving forward. In the event there is no prescription, a warning will be levied in the approval.

I feel cautious when levying errors against the plan approvals. Errors in the plan approval will block the approval of the plan, and these approval extensions cannot be easily turned off by the Eclipse user base. Remember, there are always edge cases that could happen when approving plans for emergency treatments, quality assurance, or for export to 3rd party oncology information systems. It would be detrimental to the utility of approval extensions going forward if approvals were blocked unnecessarily causing burden and delay in clinical workflows.

public class ApprovalExtension
{
    public ApprovalExtension(/* User user, IntPtr parentWindowHandle */)
    {
        // TODO: Add here the code that constructs the extension. Extension state is kept alive during PrePromote and PostPromote phases.
    }
  
    public PlanValidationResult PrePromote(PlanSetupApprovalStatus newStatus, IEnumerable<PlanSetup> plans)
    {
        PlanValidationResult result = new PlanValidationResult();
        // TODO: Add here the code that is executed before the Eclipse Planning Approval wizard is launched.
        return result;
    }
  
    public void PostPromote(PlanSetupApprovalStatus newStatus, IEnumerable<PlanSetup> plans /*, PlanValidationResult results */)
    {
        // TODO: Add here the code that is executed after the Eclipse Planning Approval wizard has been successfully completed.
    }
}

A simple set of code is added to the PrePromote method. First, it is noted that there is a collection of PlanSetup objects input into the method. This is because it is possible to approve multiple plans at once, as is the case in the approval of a Plan Sum. Here, loop through the plans and perform the check. In the first part of the code, the plan is checked to be a photon plan by checking the first non-setup beam for the existence of the letter "X" as is the case with photon energies. If that exists, check that the PhotonCalculationOption "HeterogeneityCorrection" is set to "ON". After the PlanValidationResultDetail has been constructed for this with a message, a ValidationWarning type and a custom code string, move on the check the prescription.


Remember, there are always edge cases that could happen when approving plans for emergency treatments, quality assurance, or for export to 3rd party oncology information systems. It would be detrimental to the utility of approval extensions going forward if approvals were blocked unnecessarily causing burden and delay in clinical workflows.

If the prescription is null, then we relay a warning to the user that no prescription is attached. It may not be beneficial to produce an error in this instance as prescriptions are not always attached to plans intended for approval (i.e. QA plan approvals). If the prescription does in fact exist, then check the settings of the prescription to ensure they match with the current plan. This could be basis for an error since a prescription mismatch in some parameters from the plan would be accidental and require investigation.

 PlanValidationResult result = new PlanValidationResult();
 // TODO: Add here the code that is executed before the Eclipse Planning Approval wizard is launched.

 //check that heterogeneity correction is turned on.
 foreach (var plan in plans)
 {
     //check if the plan is a photon beam.
     if (plan.Beams.First(b => !b.IsSetupField).EnergyModeDisplayName.ToUpper().Contains("X"))
     {
         if (plan.PhotonCalculationOptions["HeterogeneityCorrection"].ToString().ToUpper() != "ON")
         {
             PlanValidationResultDetail hetResult = new PlanValidationResultDetail(
                 plan.Id,
                 $"Plan '{plan.Id}' has heterogeneity correction turned off. Please turn it on before approving the plan.",
                  PlanValidationResultDetail.ResultClassification.ValidationWarning,
                  "PC001");
             result.Details.Add(hetResult);
  
         }

     }
     //check on prescription existence.
     if (plan.RTPrescription == null)
     {
         PlanValidationResultDetail rxNullResult = new PlanValidationResultDetail(
                 plan.Id,
                 $"Plan '{plan.Id}' does not have a prescription associated with it. Please add a prescription before approving the plan.",
                  PlanValidationResultDetail.ResultClassification.ValidationWarning,
                  "PC002");
			result.Details.Add(rxNullResult);
     }
     else
     {
         //check parameters of the prescription against the prescription
         var rx = plan.RTPrescription;
         //check dose per fraction
         var rxDose = rx.Targets.Max(t => t.DosePerFraction.Dose);
         if (plan.DosePerFraction.Dose != rxDose)
         {
             PlanValidationResultDetail dosePerFxResult = new PlanValidationResultDetail(
                 plan.Id,
                 $"Plan '{plan.Id}' has a dose per fraction of {plan.DosePerFraction.Dose} {plan.TotalDose.UnitAsString}, which does not match the prescription dose per fraction of {rxDose} {plan.TotalDose.UnitAsString}.\n Please correct this before approving the plan.",
                  PlanValidationResultDetail.ResultClassification.ValidationError,
                  "PC003");
             result.Details.Add(dosePerFxResult);
         }
         //Check number of Fractions
         //Check Energy
         //Check Gating
     }
 }
 return result;

Preparing the Script

When the script is ready to test and implement, it needs to first be signed. In order to sign the script, enter the Project -> Properties menu in Visual Studio. On the left side, click on the Signing tab. Check the checkbox sign the assmebly, and select New... from the available dropdown. This will allow you to choose the name of the sign name key file. In this example the name is set to the same name as the project. This generates an .snk file that will then be compiled into the binary.


ree

A note on signing assemblies

When you sign an assembly in Visual Studio, you are adding a cryptographic signature to the compiled DLL so that .NET and Windows can verify:

  1. Where it came from (authenticity / publisher identity)

  2. That it has not been modified since it was built

In .NET terms, a signed assembly is called a strong-named assembly.

A signed (strong-named) assembly includes:

  • Assembly name

  • Version number

  • Culture

  • Public key

  • Digital signature

The .snk (Strong Name Key) file contains a public/private key pair that is used for signing.

  • The private key signs the DLL at build time

  • The public key becomes embedded in the compiled assembly for verification

You never share the private key publicly. The public key is safe — it is distributed inside the DLL.

Now, you're ready to compile the script. Go ahead and compile the binary plugin using the Build menu. Once the .esapi.dll file has been compiled, its time to move it into the very specific place within the publishedscripts folder. The publishedscripts folder can be easily accessed from External Beam Planning by clicking on Tools->Scripts ensuring the System Scripts radio button is selected and then clicking Open Folder...


Within the PublishedScripts folder create a new folder called Extensions, and within that folder add a new folder with the name of the compiled assembly (without the .dll). In this instance, we will name a folder ApprovalChecks.approvalextension. Finally, add a folder with the version of the current assembly (i.e. 1.0.0.1). Copy the dll into this folder.


ree

At this point, the script is ready for approval. The approval takes place similar to any automation script that would need approval. Enter the Script Administration window from the Tools menu. From here, navigate to the Extensions folder where you saved the binary plugin. Approve the plugin, or for limited approval use the Approve for Evaluation Testing option. Click OK to close the Script Administration page.


ree

Lastly, the script extension must be registered in RT Administration.

  • Navigate to the RT Administration workspace.

  • From RT Administration click Tools -> Approval Extensions to open the Approval Extension dialog box.

  • From here, click Add and the approved approval extension scripts should be available in the dialog box.


    Note that the Approval Extensions dialog also include buttons to Invoke Earlier or Invoke Later allowing developers to tier approval extensions in a specific order when it makes sense to do so.

ree

Running the Approval Extension

In the first instance, the plan has been attached to a prescription, but the dose per fraction was modified post Rx attachment to force a mismatch between the two. The plan was also calculated without heterogeneity corrections. To test the functionality of the approval extension, the plan approval status must be changed from Unapproved to Planning approved.


Immediately after plan approval is selected, the first window of the plan approval wizard becomes available. The two custom checks from the approval extension are visible at the top of the list. They blend well with the other warnings for plan approval (i.e. tolerance tables not defined). The approval extension is blocking our plan approval due to an error with the text "Plan 'Plan1' has a dose per fraction of 200cGy, which does not match the prescription dose per fraction of 250 cGy." The next button is grayed out and unavailable.

ree

In a new plan that utilizes heterogeneity corrections in the algorithm and the correct dose per fraction, the plan approval warnings begin with the built-in warnings. To the user, they are unaware that any additional custom checks are being applied.

ree

Final Thoughts

Approval extensions are uniquely positioned to impact workflow because they provide a reliable enforcement point in the approval chain.

Examples that are now scriptable (without plan modification):

Use Case

Example

Protocol enforcement

Ensure PTV coverage or OAR dose limits documented before approval

Safety validation

Prevent approval if MU per field exceeds threshold

Audit confirmation

Require an MLC collision warning acknowledgment

Communication

Show a checklist before approving Head & Neck plans

Living documentation

Notify and log when "approved with warnings"

Instead of relying on manual decision-making or inconsistent note-taking, approval extensions allow critical safeguards to be encoded directly in the approval workflow.


V18’s Approval Extensions represent a major step toward modern, automated plan governance in Eclipse. They bridge the gap between clinical safety expectations and software-enforced validation. We're looking forward to seeing all the creative implementations of these approval extension scripts.


©2035 by NWS. Powered and secured by Wix

bottom of page