Commits

Anonymous committed 7ebe671

send ECAT sessions to prearchive (via importer), not archive

Comments (0)

Files changed (4)

src/main/java/org/nrg/dcm/Study.java

      * @param path The relative path to validate.
      * @return The relative path to the REST server URL, stripped of leading slashes.
      */
-    private String getWebAppRelativePath(final URL url, final String path) {
+    public static String getWebAppRelativePath(final URL url, final String path) {
         final StringBuilder buffer = new StringBuilder(path);
         while ('/' == buffer.charAt(0)) {
             buffer.deleteCharAt(0);
         return buffer.toString();
     }
 
-    private URL buildSessionViewURL(final URL url, final String relativePath) {
+    public static URL buildSessionViewURL(final URL url, final String relativePath) {
         final String[] components = relativePath.split("/");
         if (!"data".equals(components[0]) && !"REST".equals(components[0])) {
-            logger.warn("Strange session path {}: first component is neither \"data\" nor \"REST\"", relativePath);
+            LoggerFactory.getLogger(Study.class).warn("Strange session path {}: first component is neither \"data\" nor \"REST\"", relativePath);
         }
         if ("prearchive".equals(components[1])) {
             // prearchive sessions need some extra help for nice display
             try {
                 return new URL(url.toString() + "?screen=XDATScreen_uploaded_xnat_imageSessionData.vm");
             } catch (MalformedURLException e) {
-                logger.error("can't build prearchive session view url for " + url, e);
+                LoggerFactory.getLogger(Study.class).error("can't build prearchive session view url for " + url, e);
                 return url;
             }
         } else {
         }
     }
 
-    private static StringBuilder appendServer(final StringBuilder sb, final URL url) {
+    public static StringBuilder appendServer(final StringBuilder sb, final URL url) {
         sb.append(url.getProtocol()).append("://");
         sb.append(url.getHost());
         final int httpPort = url.getPort();
         }
         return sb;
     }
+    
+    public static StringBuilder appendServer(final StringBuilder sb, final RestServer xnat) {
+        return appendServer(sb, xnat.getURL());
+    }
 
     private static <T> StringBuilder buildHTMLFailureMessage(final StringBuilder sb, final Map<?, T> failures) {
         final Multimap<T, Object> inverse = LinkedHashMultimap.create();
         return sb;
     }
 
-    private static <T> String buildFailureMessage(final Map<?, T> failures) {
+    public static <T> String buildFailureMessage(final Map<?, T> failures) {
         final StringBuilder sb = new StringBuilder("<html>");
         buildHTMLFailureMessage(sb, failures);
         return sb.append("</html>").toString();

src/main/java/org/nrg/ecat/EcatSession.java

  */
 package org.nrg.ecat;
 
-import com.google.common.base.Strings;
-import com.google.common.collect.*;
+import static org.nrg.upload.ui.UploadAssistantApplet.XNAT_ADMIN_EMAIL_WIZ_PARAM;
+import static org.nrg.upload.ui.UploadAssistantApplet.XNAT_REST_API_WIZ_PARAM;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.text.SimpleDateFormat;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Level;
+import org.json.JSONException;
+import org.json.JSONObject;
 import org.netbeans.spi.wizard.ResultProgressHandle;
 import org.netbeans.spi.wizard.Summary;
 import org.nrg.ecat.edit.ScriptApplicator;
 import org.nrg.ecat.edit.Variable;
-import org.nrg.net.ConflictHttpException;
+import org.nrg.framework.logging.Analytics;
+import org.nrg.net.HttpException;
 import org.nrg.net.HttpURLConnectionProcessor;
+import org.nrg.net.JSONRequestConnectionProcessor;
 import org.nrg.net.RestServer;
-import org.nrg.net.StringResponseProcessor;
-import org.nrg.upload.data.*;
+import org.nrg.upload.data.AssignedSessionVariable;
+import org.nrg.upload.data.Project;
+import org.nrg.upload.data.Session;
+import org.nrg.upload.data.SessionVariable;
+import org.nrg.upload.data.SessionVariableNames;
+import org.nrg.upload.data.Subject;
+import org.nrg.upload.data.UploadFailureHandler;
 import org.nrg.upload.ui.AssignSessionVariablesPage;
 import org.nrg.upload.ui.SelectProjectPage;
 import org.nrg.upload.ui.SelectSubjectPage;
+import org.nrg.upload.ui.UploadAssistantApplet;
 import org.nrg.upload.ui.UploadResultPanel;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.util.*;
+import com.google.common.base.Strings;
+import com.google.common.collect.LinkedHashMultimap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Multimap;
+import com.google.common.collect.Multimaps;
 
 /**
  * @author Kevin A. Archie <karchie@wustl.edu>
  */
 public final class EcatSession implements Session {
     private static final String LINE_SEPARATOR = System.getProperty("line.separator");
-    private static final String URL_ENCODE_SCHEME = "UTF-8";
     private static final String FORMAT = "ECAT";
     private static final String MODALITY = "PET";
+    private static final String TIMESTAMP_FORMAT = "yyyyMMdd_HHmmss";
+    private static final String URL_ENCODE_SCHEME = "UTF-8";
 
     private final Logger logger = LoggerFactory.getLogger(EcatSession.class);
     private final MatrixDataFile first;
             return Collections.emptyList();
         }
         final List<SessionVariable> sessionVars = Lists.newArrayList();
+        sessionVars.add(new AssignedSessionVariable(SessionVariableNames.MODALITY_LABEL, MODALITY, true));
         for (final Variable ev : evs) {
             sessionVars.add(EcatSessionVariable.getSessionVariable(ev));
         }
         return builder.toString();
     }
 
+    private final static String makeTimestamp() {
+        return new SimpleDateFormat(TIMESTAMP_FORMAT).format(new Date());        
+    }
+    
     /* (non-Javadoc)
      * @see org.nrg.upload.data.Session#uploadTo(java.util.Map, org.netbeans.spi.wizard.ResultProgressHandle)
      */
         final Project project = (Project)params.get(SelectProjectPage.PRODUCT_NAME);
         final Subject subject = (Subject)params.get(SelectSubjectPage.PRODUCT_NAME);
         final SessionVariable session = (SessionVariable) params.get(SessionVariableNames.SESSION_LABEL);
+        final String sessionID = session.getValue();
 
+        final String timestamp = makeTimestamp();
         final RestServer xnat = project.getRestServer();
 
         final String sessionLabel = session.getValue();
         progress.setBusy("Creating session " + sessionLabel);
+        
+        /*
         final String path = String.format("REST/projects/%s/subjects/%s/experiments/%s",
                 project, subject, sessionLabel);
 
             progress.failed(e.getMessage(), true);	// TODO: try again?
             return false;
         }
+        */
+        
+        final String path = String.format("/data/services/import?dest=/prearchive/projects/%s/%s/%s",
+                project, timestamp, session.getValue());
 
         // add scans to session, and the data file to each scan
         int i = 0;
             final File f = dataFile.getFile();
             TRY_FILE: while (true) {
                 try {
-                    final String scan = String.format("%s/scans/%d", path, ++i);
-                    logger.trace("creating scan {}", i);
-                    progress.setBusy(String.format("Creating scan %d/%d", i, size));
-                    xnat.doPut(scan + "?xsiType=xnat:petScanData");
+                    final String scan = String.format("%s&SUBJECT_ID=%s&overwrite=append&xnat:petSessionData/scans/scan/ID=%d", path, subject.getLabel(), ++i);
                     progress.setProgress(String.format("Uploading scan %d/%d", i, size), i - 1, size);
-                    final HttpURLConnectionProcessor processor = new ModifyingUploadProcessor(f, progress, project.toString(), subject.getLabel(), session.getValue());
+                    final HttpURLConnectionProcessor processor = new ModifyingUploadProcessor(f, progress, project.toString(), subject.getLabel(), sessionID);
                     logger.trace("uploading {} as scan {}", f, i);
                     final long start = new Date().getTime();
-                    String encodedPath = String.format("%s/files/%s?inbody=true&format=ECAT&content=RAW", scan, URLEncoder.encode(f.getName(), "UTF-8"));
-                    xnat.doPut(encodedPath, processor);
+                    xnat.doPost(scan, processor);
                     logger.trace("upload successful: {} bytes in {} seconds", size, (new Date().getTime() - start)/1000L);
                     break TRY_FILE;
-                } catch (Exception e) {
-                    if (failureHandler.shouldRetry(dataFile.getFile(), e)) {
-                        logger.info("upload failed, retrying", e);
+                } catch (Throwable t) {
+                    if (failureHandler.shouldRetry(dataFile.getFile(), t)) {
+                        logger.info("upload failed, retrying", t);
                     } else {
                         final Map<File,Object> failures = Maps.newLinkedHashMap();
-                        failures.put(f, e);
+                        failures.put(f, t);
                         final StringBuilder message = new StringBuilder("user canceled operation after errors:");
                         message.append(LINE_SEPARATOR);
                         buildFailureMessage(message, failures);
             }
         }
 
-
-        // Commit session
+        final Map<?,?> failures = Maps.newHashMap();
+        closeSession(String.format("/data/prearchive/projects/%s/%s/%s", project, timestamp, session.getValue()), params, progress, failures);
+        
+        /*
+       // Commit session
         try {
             progress.setBusy(String.format("Committing session %s (%s)", sessionLabel, sessionID));
             xnat.doPut(path + "?pullDataFromHeaders=true");
             progress.failed(e.getMessage(), true);	// TODO: try again?
             return false;
         }
+        */
+        
+        return true;
+    }
+
+    // TODO: this is duplicate of method in org.nrg.dcm.Study
+    private JSONObject buildCommitEntity(final Map<?, ?> params) {
+        final JSONObject entity = new JSONObject();
+        final Collection<?> vars = (Collection<?>) params.get(AssignSessionVariablesPage.PRODUCT_NAME);
+        if (null == vars) {
+            logger.error("session variables not assigned in {}", params);
+        } else {
+            for (final Object o : vars) {
+                if (o instanceof SessionVariable) {
+                    final SessionVariable v = (SessionVariable) o;
+                    final String path = v.getExportField();
+                    if (!Strings.isNullOrEmpty(path)) {
+                        try {
+                            entity.put(path, v.getValue());
+                        } catch (JSONException exception) {
+                            String message = "unable to assign session variable " + path;
+                            logger.error(message, exception);
+                            Analytics.enter(UploadAssistantApplet.class, Level.ERROR, message, exception);
+                        }
+                    }
+                }
+            }
+        }
+        logger.trace("Built commit entity: {}", entity);
+        Analytics.enter(UploadAssistantApplet.class, String.format("Built commit entity: %s", entity));
+        return entity;
+    }
+
+    // TODO: this is (almost) duplicate of method in org.nrg.dcm.Study
+    private boolean closeSession(final String path, final Map<?, ?> params, final ResultProgressHandle progress, final Map<?, ?> failures) {
+        final String adminEmail = (String) params.get(XNAT_ADMIN_EMAIL_WIZ_PARAM);
+        final String session = ((SessionVariable) params.get(SessionVariableNames.SESSION_LABEL)).getValue();
+        final RestServer xnat = (RestServer) params.get(XNAT_REST_API_WIZ_PARAM);
+
+        // Close session and return result
+        try {
+            if (failures.isEmpty()) {
+                progress.setBusy("Committing session");
+                logger.trace("committing session {}", path);
+                final JSONRequestConnectionProcessor handler = new JSONRequestConnectionProcessor(buildCommitEntity(params));
+                
+                String queryParams = "?action=commit";
+                //add visit
+                if(null != params.get(SessionVariableNames.VISIT_LABEL) && !Strings.isNullOrEmpty(((AssignedSessionVariable)params.get(SessionVariableNames.VISIT_LABEL)).getValue())){
+                    queryParams += "&VISIT=" + ((AssignedSessionVariable)params.get(SessionVariableNames.VISIT_LABEL)).getValue();
+                }
+                //add protocol
+                if(null != params.get(SessionVariableNames.PROTOCOL_LABEL) && !Strings.isNullOrEmpty(((AssignedSessionVariable)params.get(SessionVariableNames.PROTOCOL_LABEL)).getValue())){
+                    queryParams += "&PROTOCOL=" + ((AssignedSessionVariable)params.get(SessionVariableNames.PROTOCOL_LABEL)).getValue();
+                }
+                xnat.doPost(path + queryParams, handler);
+
+                String response = handler.getResponseEntity();
+                String resultPath = org.nrg.dcm.Study.getWebAppRelativePath(xnat.getURL(), response);
+              
+                final URL result = new URL(xnat.getURL() + "/" + resultPath);
+
+                // TODO: build summary, notify user
+                final UploadResultPanel resultPanel = new UploadResultPanel(session, org.nrg.dcm.Study.buildSessionViewURL(result, resultPath));
+                progress.finished(Summary.create(resultPanel, path));
+                return true;
+            } else {
+                progress.failed(org.nrg.dcm.Study.buildFailureMessage(failures), false);
+                return false;
+            }
+        } catch (JSONException e) {
+            logger.error("unable to write commit request entity", e);
+            return false;
+        } catch (HttpException e) {
+            logger.error("session commit failed", e);
+            switch (e.getResponseCode()) {
+                case HttpURLConnection.HTTP_NOT_FOUND: {
+                    final StringBuilder sb = new StringBuilder("<h3>Resource not found (404)</h3>");
+                    sb.append("<p>The server at ");
+                    org.nrg.dcm.Study.appendServer(sb, xnat);
+                sb.append(" is accessible but reports that the session resource ");
+                    sb.append(path);
+                    sb.append(" does not exist.</p>");
+                    sb.append("<p>Contact the administrator ");
+                    sb.append("<").append(adminEmail).append(">");
+                    sb.append(" for help.</p>");
+                    progress.failed(sb.toString(), true);
+                    return false;
+                }
+
+                case HttpURLConnection.HTTP_INTERNAL_ERROR: {
+                    final StringBuilder sb = new StringBuilder("<h3>Internal Server Error (500)</h3>");
+                    sb.append("<p>The server at ");
+                    org.nrg.dcm.Study.appendServer(sb, xnat);
+                    sb.append(" is accessible but was unable to commit the requested session");
+                    sb.append(" due to an internal error.</p>");
+                    sb.append("<p>Please contact the administrator ");
+                    sb.append("<").append(adminEmail).append(">");
+                    sb.append(" for help.");
+                    sb.append(" A detailed description of the problem should be available");
+                    sb.append(" in the DICOM receiver log or the XNAT logs.</p>");
+                    progress.failed(sb.toString(), true);
+                    return false;
+                }
+
+                case HttpURLConnection.HTTP_CONFLICT: {
+                    final StringBuilder sb = new StringBuilder("<h3>Session data conflict</h3>");
+                    sb.append("<p>The server at ");
+                    org.nrg.dcm.Study.appendServer(sb, xnat);
+                    sb.append(" reports a conflict between the uploaded data and a session in the archive.</p>");
+                    sb.append("<p>All or part of this session was previously uploaded. Go to the prearchive page ");
+                    sb.append("to archive the data just uploaded as a new session, or to merge it into an existing session.");
+                    progress.failed(sb.toString(), true);
+                    return false;
+                }
+
+                default: {
+                    final StringBuilder sb = new StringBuilder("<h3>Unexpected error ");
+                    sb.append(e.getResponseCode()).append(": ");
+                    sb.append(e.getMessage()).append("</h3>");
+                    sb.append("<p>Unable to commit uploaded session</p>");
+                    sb.append("<p>Please contact your XNAT administrator ");
+                    sb.append("<").append(adminEmail).append(">");
+                    sb.append(" for help.</p>");
+                    progress.failed(sb.toString(), true);
+                    return false;
+                }
+            }
+        } catch (IOException e) {
+            logger.error("Session commit failed", e);
+            final StringBuilder sb = new StringBuilder("<h3>Communications error</h3>");
+            sb.append("<p>The server at ");
+            org.nrg.dcm.Study.appendServer(sb, xnat);
+            sb.append(" is inaccessible (");
+            sb.append(e.getMessage());
+            sb.append("). Please contact your XNAT administrator ");
+            sb.append("<").append(adminEmail).append(">");
+            sb.append(" for help.</p>");
+            progress.failed(sb.toString(), false);
+            return false;
+        } catch (Throwable t) {
+            logger.error("Session commit failed", t);
+            final StringBuilder sb = new StringBuilder("<h3>Error in applet</h3>");
+            sb.append("<p>An error in the uploader (").append(t);
+            sb.append(" prevented the session from being committed.");
+            sb.append(" Please contact your XNAT administrator ");
+            sb.append("<").append(adminEmail).append(">");
+            sb.append(" for help.</p>");
+            progress.failed(sb.toString(), false);
+            return false;
+        }
     }
 
     private Object getUserCanceledFailure() {

src/main/java/org/nrg/ecat/ModifyingUploadProcessor.java

 /**
- * Copyright (c) 2009-2010 Washington University
+ * Copyright (c) 2009-2012 Washington University
  */
 package org.nrg.ecat;
 
 import java.net.HttpURLConnection;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipOutputStream;
 
 import org.netbeans.spi.wizard.ResultProgressHandle;
 import org.nrg.ecat.var.Variable;
  *
  */
 public class ModifyingUploadProcessor implements HttpURLConnectionProcessor {
-	private static final String CONTENT_LENGTH_HEADER = "Content-Length";
 	private static final String CONTENT_TYPE_HEADER = "Content-Type";
-	private static final String CONTENT_TYPE = "application/x-ecat";
+	private static final String CONTENT_TYPE = "application/zip";
 	private static final Variable[] toClear = {
 		PATIENT_AGE,
 		PATIENT_HEIGHT,
 	};
 	
 	private final Logger logger = LoggerFactory.getLogger(ModifyingUploadProcessor.class);
+	private final String name;
 	private final InputStream in;
 	private final int size;
 	private final ResultProgressListener progress;
 		if (size < 0) {
 			throw new UnsupportedOperationException("cannot upload files with size beyond integer range");
 		}
+		name = f.getName();
 		this.progress = new ResultProgressListener(progress, 0, size);
 		modifications = new ArrayList<HeaderModification>();
 		modifications.add(STUDY_DESCRIPTION.createValueModification(project));
 	 * @see org.nrg.net.HttpURLConnectionProcessor#prepare(java.net.HttpURLConnection)
 	 */
 	public void prepare(final HttpURLConnection connection) throws IOException {
-		connection.setDoOutput(true);
-		connection.setRequestProperty(CONTENT_TYPE_HEADER, CONTENT_TYPE);
-		connection.setRequestProperty(CONTENT_LENGTH_HEADER, String.valueOf(size));
-		connection.setFixedLengthStreamingMode(size);
-		connection.connect();
-		try {
-			final OutputStream out = connection.getOutputStream();
-			try {
-				MatrixData.copyWithModifications(out, in, modifications, progress);
-				out.flush();
-			} catch (final Throwable t) {
-				logger.error("copy operation failed", t);
-			} finally {
-				out.close();
-			}
-		} finally {
-			in.close();
-		}
+	    connection.setDoOutput(true);
+	    connection.setRequestProperty(CONTENT_TYPE_HEADER, CONTENT_TYPE);
+	    connection.connect();
+	    IOException ioexception = null;
+	    try {
+	        final OutputStream out = connection.getOutputStream();
+	        try {
+	            final ZipOutputStream zout = new ZipOutputStream(out);
+	            try {
+	                final ZipEntry ze = new ZipEntry(name);
+	                zout.putNextEntry(ze);
+	                MatrixData.copyWithModifications(zout, in, modifications, progress);
+	                zout.closeEntry();
+	            } catch (IOException e) {
+	                throw ioexception = e;
+	            } finally {
+	                try {
+	                    zout.close();
+	                } catch (IOException e) {
+	                    if (null == ioexception) {
+	                        throw ioexception = e;
+	                    } else {
+	                        logger.error("unable to close ZipOutputStream", e);
+	                        throw ioexception;
+	                    }
+	                }
+	            }
+	        } catch (final Throwable t) {
+	            logger.error("copy operation failed", t);
+	        } finally {
+	            try {
+	            out.close();
+	            } catch (IOException e) {
+	                if (null == ioexception) {
+	                    throw ioexception = e;
+	                } else {
+	                    logger.error("unable to close HttpURLConnection output stream", e);
+	                    throw ioexception;
+	                }
+	            }
+	        }
+	    } finally {
+	        in.close();
+	    }
 	}
 
 	/* (non-Javadoc)

src/main/java/org/nrg/upload/ui/AssignSessionVariablesPage.java

 /**
- * Copyright (c) 2009-2011 Washington University
+ * Copyright (c) 2009-2012 Washington University
  */
 package org.nrg.upload.ui;
 
-import com.google.common.base.Joiner;
-import com.google.common.base.Strings;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
-import com.google.common.collect.Sets;
+import static org.nrg.upload.data.SessionVariableNames.MODALITY_LABEL;
+import static org.nrg.upload.data.SessionVariableNames.PREDEF_SESSION;
+import static org.nrg.upload.data.SessionVariableNames.PROJECT;
+import static org.nrg.upload.data.SessionVariableNames.SESSION_LABEL;
+import static org.nrg.upload.data.SessionVariableNames.SUBJECT;
+
+import java.awt.BorderLayout;
+import java.awt.Component;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.Date;
+import java.util.LinkedList;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedSet;
+
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+
 import org.apache.commons.lang.time.DateUtils;
 import org.netbeans.spi.wizard.Wizard;
 import org.netbeans.spi.wizard.WizardPage;
 import org.nrg.ecat.EcatSessionVariable;
 import org.nrg.framework.constants.AutoArchive;
 import org.nrg.framework.constants.PrearchiveCode;
-import org.nrg.upload.data.*;
+import org.nrg.upload.data.AssignedSessionVariable;
+import org.nrg.upload.data.ExcludingValueValidator;
+import org.nrg.upload.data.IndexedDependentSessionVariable;
+import org.nrg.upload.data.LabelValueValidator;
+import org.nrg.upload.data.Project;
+import org.nrg.upload.data.Session;
+import org.nrg.upload.data.SessionVariable;
 import org.nrg.upload.data.SessionVariable.InvalidValueException;
+import org.nrg.upload.data.SessionVariableConsumer;
+import org.nrg.upload.data.SessionVariableNames;
+import org.nrg.upload.data.Subject;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import javax.swing.*;
-import java.awt.*;
-import java.beans.PropertyChangeEvent;
-import java.beans.PropertyChangeListener;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.*;
-
-import static org.nrg.upload.data.SessionVariableNames.*;
+import com.google.common.base.Joiner;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
 
 /**
  * @author Kevin A. Archie <karchie@wustl.edu>
  *
  */
 public final class AssignSessionVariablesPage extends WizardPage implements SessionVariableConsumer, PropertyChangeListener {
+    private static final long serialVersionUID = -2786166512597463435L;
+
     public static final String PRODUCT_NAME = "*session-variables*";
 
     private static final String STEP_DESCRIPTION = "Enter session details";
     private static final String DUP_SESSION_ID_TITLE = "Session ID Already Exists";
     private static final String DUP_SESSION_ID_MESSAGE =
             "<html><p>The session ID you've specified is already present in your project.</p>" +
-            "<p>Would you like to overwrite the existing session, append to the existing</p>" +
-            "<p>session, or provide a new session ID?</p></html>";
+                    "<p>Would you like to overwrite the existing session, append to the existing</p>" +
+                    "<p>session, or provide a new session ID?</p></html>";
     private static final String[] DUP_SESSION_ID_OPTIONS = { "Overwrite existing", "Append to existing", "Provide new session ID" };
     private static final String NULL_SESSION_DATE_TITLE = "Session Has No Associated Date";
     private static final String NULL_SESSION_DATE_MESSAGE =
             "<html><p>The session you selected has no date associated with it, but you specified a date" +
-            "<p>for the session. This can happen when data has been anonymized or when scan data has been" +
-            "<p>corrupted. Please verify that you've selected the correct session for upload. If not," +
-            "<p>click the <b>Prev</b> button to return to the previous screen to select a different" +
-            "<p>session or click <b>Next</b> or <b>Finish</b> to continue with the existing session.</p></html>";
+                    "<p>for the session. This can happen when data has been anonymized or when scan data has been" +
+                    "<p>corrupted. Please verify that you've selected the correct session for upload. If not," +
+                    "<p>click the <b>Prev</b> button to return to the previous screen to select a different" +
+                    "<p>session or click <b>Next</b> or <b>Finish</b> to continue with the existing session.</p></html>";
     private static final String INVALID_SESSION_DATE_TITLE = "Invalid Session Date";
     private static final String INVALID_SESSION_DATE_MESSAGE =
             "<html><p>The date you indicated for your scan session does not match</p>" +
-                  "<p>the date of the selected session. Please re-check the date</p>" +
-                  "<p>for your new session and start over or click <b>Prev</b> and</p>"  +
-                  "<p>select another session that matches the indicated date.</p></html>";
+                    "<p>the date of the selected session. Please re-check the date</p>" +
+                    "<p>for your new session and start over or click <b>Prev</b> and</p>"  +
+                    "<p>select another session that matches the indicated date.</p></html>";
     private static final String UNEXPECTED_MODALITY_TITLE = "Unexpected Modality";
     private static final String UNEXPECTED_MODALITY_MESSAGE = 
-    		"<html><p>The expected modality does not match that of the session.</p>" +
-    				"<p>Please re-check the modality of your new session and start</p>" +
+            "<html><p>The expected modality does not match that of the session.</p>" +
+                    "<p>Please re-check the modality of your new session and start</p>" +
                     "<p>over or click <b>Prev</b> and select another session that matches</p>"  +
                     "<p>the indicated modality.</p></html>";
 
     private Date confirmedDate;
     private boolean isAutoArchiving = false;
 
-    private static final GridBagConstraints SPANNING = new GridBagConstraints() {{
-        gridx = 0;
-        gridwidth = 2;
-        insets = new Insets(8, 0, 0, 0);
-    }};
+    private static final GridBagConstraints SPANNING = new GridBagConstraints() {
+        private static final long serialVersionUID = 5114328188210435952L;
+        {
+            gridx = 0;
+            gridwidth = 2;
+            insets = new Insets(8, 0, 0, 0);
+        }};
 
-    //If d1's hours and minutes are both == 0 we assume we're testing
-    //to see if both dates occured on the same day.  If there is data
-    //in the hours and minutes, we return true if both dates are within
-    // a 61 minute window of each other.
-    private boolean isSessionDateOk(Date d1, Date d2){
-    	Calendar cal = Calendar.getInstance(); 
-    	cal.setTime(d1);
-    	int hour = cal.get(Calendar.HOUR_OF_DAY);
-    	int minute = cal.get(Calendar.MINUTE);
-    	if(hour == 0 && minute == 0){
-    		return DateUtils.isSameDay(d1, d2);
-    	} else {
-    		//check the 61 minute window
-    		return Math.abs(d1.getTime() - d2.getTime()) < 3660000;    		
-    	}
-    }
-    /**
-     * Gets the description of this step for the wizard framework.
-     * @return The step description.
-     */
-    public static String getDescription() {
-        return STEP_DESCRIPTION;
-    }
-
-    /**
-     * Default constructor.
-     */
-    public AssignSessionVariablesPage() {
-        setLayout(new BorderLayout());
-        setLongDescription(LONG_DESCRIPTION);
-    }
-
-    /**
-     * Implementation of the method.
-     * @see org.nrg.upload.data.SessionVariableConsumer#update(org.nrg.upload.data.SessionVariable, boolean)
-     */
-    @Override
-    public void update(final SessionVariable v, final boolean isValidValue) {
-        logger.trace("{} updated to {}", v, isValidValue ? "valid" : "invalid");
-        if (isValidValue) {
-            invalid.remove(v);
-        } else {
-            invalid.add(v);
-            setProblem(v.getValueMessage());
-        }
-        setProblem(validateContents(null, null));
-    }
-
-    /**
-     * Verifies whether or not user wants to proceed in the case of a verification date mismatch.
-     * @param stepName The name of the current step.
-     * @param settings Any settings for the current step.
-     * @param wizard The current wizard.
-     * @return Returns <b>WizardPanelNavResult.REMAIN_ON_PAGE</b> if the user doesn't confirm, <b>WizardPanelNavResult.PROCEED</b> otherwise.
-     */
-    @Override
-    public WizardPanelNavResult allowNext(String stepName, Map settings, Wizard wizard) {
-        return sessionDate != null && isSessionDateOk(confirmedDate, sessionDate) ? WizardPanelNavResult.PROCEED : WizardPanelNavResult.REMAIN_ON_PAGE;
-    }
-
-    /**
-     * Verifies whether or not user wants to proceed in the case of a verification date mismatch.
-     * @param stepName The name of the current step.
-     * @param settings Any settings for the current step.
-     * @param wizard The current wizard.
-     * @return Returns <b>WizardPanelNavResult.REMAIN_ON_PAGE</b> if the user doesn't confirm, <b>WizardPanelNavResult.PROCEED</b> otherwise.
-     */
-    @Override
-    public WizardPanelNavResult allowFinish(String stepName, Map settings, Wizard wizard) {
-        return WizardPanelNavResult.PROCEED;
-    }
-
-    /**
-     * Implements the {@link PropertyChangeListener#propertyChange(PropertyChangeEvent)} method. This is used to
-     * prompt the wizard framework to take notice when something has changed on the page.
-     * @param event    The property change event.
-     */
-    @Override
-    public void propertyChange(PropertyChangeEvent event) {
-        userInputReceived(this, event);
-    }
-
-    /*
-     * (non-Javadoc)
-     * @see org.netbeans.spi.wizard.WizardPage#recycle()
-     */
-    @Override
-    protected void recycle() {
-        removeAll();
-    }
-
-    /*
-     * (non-Javadoc)
-     * @see org.netbeans.spi.wizard.WizardPage#renderingPage()
-     */
-    @Override
-    protected void renderingPage() {
-        initialize();
-
-        String expectedModality = (String)this.getWizardData(UploadAssistantApplet.EXPECTED_MODALITY_LABEL);
-        Set<String> modalities = session.getModalities();
-
-        if(expectedModality != null && modalities != null ){
-            if("PT".equalsIgnoreCase(expectedModality) || "PET".equalsIgnoreCase(expectedModality)){
-            	if(! (modalities.contains("PT") || modalities.contains("PET"))){	
-            		JOptionPane.showMessageDialog(this, UNEXPECTED_MODALITY_MESSAGE, UNEXPECTED_MODALITY_TITLE, JOptionPane.WARNING_MESSAGE);
-            	}            	
+        //If d1's hours and minutes are both == 0 we assume we're testing
+        //to see if both dates occured on the same day.  If there is data
+        //in the hours and minutes, we return true if both dates are within
+        // a 61 minute window of each other.
+        private boolean isSessionDateOk(Date d1, Date d2){
+            Calendar cal = Calendar.getInstance(); 
+            cal.setTime(d1);
+            int hour = cal.get(Calendar.HOUR_OF_DAY);
+            int minute = cal.get(Calendar.MINUTE);
+            if(hour == 0 && minute == 0){
+                return DateUtils.isSameDay(d1, d2);
             } else {
-            	if(! modalities.contains(expectedModality)){	
-            		JOptionPane.showMessageDialog(this, UNEXPECTED_MODALITY_MESSAGE, UNEXPECTED_MODALITY_TITLE, JOptionPane.WARNING_MESSAGE);
-            	}
+                //check the 61 minute window
+                return Math.abs(d1.getTime() - d2.getTime()) < 3660000;    		
             }
         }
-        if (sessionDate == null && confirmedDate != null) {
-            JOptionPane.showMessageDialog(this, NULL_SESSION_DATE_MESSAGE, NULL_SESSION_DATE_TITLE, JOptionPane.WARNING_MESSAGE);
-        } else if (!isSessionDateOk(confirmedDate, sessionDate)) {
-            JOptionPane.showMessageDialog(this, INVALID_SESSION_DATE_MESSAGE, INVALID_SESSION_DATE_TITLE, JOptionPane.ERROR_MESSAGE);
-        } else {
-            add(addContent(new JPanel(new GridBagLayout())), BorderLayout.CENTER);
-        }
-    }
-
-    /*
-     * (non-Javadoc)
-     * @see org.netbeans.spi.wizard.WizardPage#validateContents(java.awt.Component, java.lang.Object)
-     */
-    @Override
-    protected String validateContents(final Component component, final Object event) {
-        if (!isSessionDateOk(confirmedDate, sessionDate)) {
-            // We have to go through this whole rigamarole to deal with the Java Date class's inadequacies and general crappiness.
-            // The year being set by dcm4che seems to absolute, i.e. setting 2012 when it really means 2012. The nerve. Instead,
-            // according to Java, it should be setting the offset from 1900, i.e. 112, when it means 2012. But we get what we get.
-            // So, check for a year greater than 1900 and, if found, offset by -1900 and hope like crazy that that's the right thing.
-            Calendar calendar = Calendar.getInstance();
-            calendar.setTime(confirmedDate);
-//            int year = calendar.get(Calendar.YEAR);
-//            if (year > 1900) {
-//                calendar.set(Calendar.YEAR, year - 1900);
-//            }
-            return "Click the Previous button and select a session with scan date of " +  FORMATTER.format(calendar.getTime());
+        /**
+         * Gets the description of this step for the wizard framework.
+         * @return The step description.
+         */
+        public static String getDescription() {
+            return STEP_DESCRIPTION;
         }
 
-        // If verifyDate is empty, we won't allow user to proceed, but we won't do any other validation.
-        boolean stopForDuplicateSessionId = stopForDuplicateSessionId();
-        if (stopForDuplicateSessionId) {
-            return "You must either select another session, change the session ID, or indicate how you want to handle the duplicate session ID for your auto-archiving project.";
-        }
-        final SortedSet<String> names = Sets.newTreeSet();
-        for (final SessionVariable v : invalid) {
-            names.add(v.getName());
-        }
-        if (names.size() > 0) {
-            final StringBuilder buffer = new StringBuilder("Some fields have invalid values: ");
-            final Joiner joiner = Joiner.on(", ");
-            return joiner.appendTo(buffer, names).toString();
-        }
-        return null;
-    }
-
-    /**
-     * Initializes all incoming data from the wizard's data map.
-     */
-    private void initialize() {
-        project = (Project) getWizardData(SelectProjectPage.PRODUCT_NAME);
-        subject = (Subject) getWizardData(SelectSubjectPage.PRODUCT_NAME);
-        session = (Session) getWizardData(SelectSessionPage.PRODUCT_NAME);
-        sessionDate = session.getDateTime();
-        sessionLabel = (SessionVariable) getWizardData(SessionVariableNames.SESSION_LABEL);
-        Object candidate = getWizardData(ConfirmSessionDatePage.PRODUCT_NAME);
-        // If we got a candidate for the confirmed date...
-        if (candidate != null) {
-            // See if it's a date.
-            if (candidate instanceof Date) {
-                // If so, stash that.
-                confirmedDate = (Date) candidate;
-            }
-            // If it wasn't a date, check if it's a string and is the no_session_date qualifier.
-            else if (candidate instanceof String && "no_session_date".equals(candidate)) {
-                // So if it's no session date, there's a good chance the session date is null. If so, initialize both
-                // of the dates so that they're matching and we can pass through the date-check logic cleanly.
-                if (sessionDate == null) {
-                    sessionDate = new Date();
-                }
-                confirmedDate = sessionDate;
-            }
-        }
-    }
-
-    private JPanel addContent(final JPanel panel) {
-        logger.trace("adding content");
-
-        try {
-            isAutoArchiving = project.getPrearchiveCode() != PrearchiveCode.Manual;
-        } catch (Exception exception) {
-            logger.warn("Error when retrieving project prearchive code", exception);
+        /**
+         * Default constructor.
+         */
+        public AssignSessionVariablesPage() {
+            setLayout(new BorderLayout());
+            setLongDescription(LONG_DESCRIPTION);
         }
 
-        final Map<String,SessionVariable> predefs = Maps.newLinkedHashMap();
-        predefs.put(PROJECT, new AssignedSessionVariable(PROJECT, project.toString()).fixValue());
-        predefs.put(SUBJECT, new AssignedSessionVariable(SUBJECT, subject.toString()).fixValue());
-        SessionVariable vSession = (SessionVariable) getWizardData(PREDEF_SESSION);
-        if (null != vSession) {
-            predefs.put(SESSION_LABEL, vSession);
+        /**
+         * Implementation of the method.
+         * @see org.nrg.upload.data.SessionVariableConsumer#update(org.nrg.upload.data.SessionVariable, boolean)
+         */
+        @Override
+        public void update(final SessionVariable v, final boolean isValidValue) {
+            logger.trace("{} updated to {}", v, isValidValue ? "valid" : "invalid");
+            if (isValidValue) {
+                invalid.remove(v);
+            } else {
+                invalid.add(v);
+                setProblem(v.getValueMessage());
+            }
+            setProblem(validateContents(null, null));
         }
-        
-        final LinkedList<SessionVariable> vars = Lists.newLinkedList(session.getVariables(getWizardDataMap()));
-        putWizardData(PRODUCT_NAME, vars);
-        logger.trace("initialized session variables: {}",  vars);
 
-        final Set<String> modalities = session.getModalities();
-        final String leadModality = SOPModel.getLeadModality(modalities);
+        /**
+         * Verifies whether or not user wants to proceed in the case of a verification date mismatch.
+         * @param stepName The name of the current step.
+         * @param settings Any settings for the current step.
+         * @param wizard The current wizard.
+         * @return Returns <b>WizardPanelNavResult.REMAIN_ON_PAGE</b> if the user doesn't confirm, <b>WizardPanelNavResult.PROCEED</b> otherwise.
+         */
+        @Override
+        public WizardPanelNavResult allowNext(String stepName, @SuppressWarnings("rawtypes") Map settings, Wizard wizard) {
+            return sessionDate != null && isSessionDateOk(confirmedDate, sessionDate) ? WizardPanelNavResult.PROCEED : WizardPanelNavResult.REMAIN_ON_PAGE;
+        }
 
-        // Strip project and subject out of the List: these are immutable and we'll
-        // display them separately.  Also check whether session has been defined;
-        // we'll use the existing variable if so, or add a new one if not.
-        SessionVariable modalityLabel = null;
-        for (final ListIterator<SessionVariable> i = vars.listIterator(); i.hasNext(); ) {
-            final SessionVariable v = i.next();
-            final String name = v.getName();
-            if (SESSION_LABEL.equals(name)) {
-                vSession = v;
-            } // no else clause: session might also be a predef and we want normal predef handling
+        /**
+         * Verifies whether or not user wants to proceed in the case of a verification date mismatch.
+         * @param stepName The name of the current step.
+         * @param settings Any settings for the current step.
+         * @param wizard The current wizard.
+         * @return Returns <b>WizardPanelNavResult.REMAIN_ON_PAGE</b> if the user doesn't confirm, <b>WizardPanelNavResult.PROCEED</b> otherwise.
+         */
+        @Override
+        public WizardPanelNavResult allowFinish(String stepName, @SuppressWarnings("rawtypes") Map settings, Wizard wizard) {
+            return WizardPanelNavResult.PROCEED;
+        }
 
-            if (predefs.containsKey(name)) {
-                final SessionVariable predef = predefs.get(name);
-                logger.trace("found predefined variable {} in script", v);
-                i.remove();
-                try {
-                    v.fixValue(predef.getValue());
-                } catch (InvalidValueException e) {
-                    throw new RuntimeException(e);
+        /**
+         * Implements the {@link PropertyChangeListener#propertyChange(PropertyChangeEvent)} method. This is used to
+         * prompt the wizard framework to take notice when something has changed on the page.
+         * @param event    The property change event.
+         */
+        @Override
+        public void propertyChange(PropertyChangeEvent event) {
+            userInputReceived(this, event);
+        }
+
+        /*
+         * (non-Javadoc)
+         * @see org.netbeans.spi.wizard.WizardPage#recycle()
+         */
+        @Override
+        protected void recycle() {
+            removeAll();
+        }
+
+        /*
+         * (non-Javadoc)
+         * @see org.netbeans.spi.wizard.WizardPage#renderingPage()
+         */
+        @Override
+        protected void renderingPage() {
+            initialize();
+
+            String expectedModality = (String)this.getWizardData(UploadAssistantApplet.EXPECTED_MODALITY_LABEL);
+            Set<String> modalities = session.getModalities();
+
+            if(expectedModality != null && modalities != null ){
+                if("PT".equalsIgnoreCase(expectedModality) || "PET".equalsIgnoreCase(expectedModality)){
+                    if(! (modalities.contains("PT") || modalities.contains("PET"))){	
+                        JOptionPane.showMessageDialog(this, UNEXPECTED_MODALITY_MESSAGE, UNEXPECTED_MODALITY_TITLE, JOptionPane.WARNING_MESSAGE);
+                    }            	
+                } else {
+                    if(! modalities.contains(expectedModality)){	
+                        JOptionPane.showMessageDialog(this, UNEXPECTED_MODALITY_MESSAGE, UNEXPECTED_MODALITY_TITLE, JOptionPane.WARNING_MESSAGE);
+                    }
                 }
-                predefs.put(v.getName(), v);
-            } else if (MODALITY_LABEL.equals(name)) {
-                modalityLabel = v;   // Process this later
+            }
+            if (sessionDate == null && confirmedDate != null) {
+                JOptionPane.showMessageDialog(this, NULL_SESSION_DATE_MESSAGE, NULL_SESSION_DATE_TITLE, JOptionPane.WARNING_MESSAGE);
+            } else if (!isSessionDateOk(confirmedDate, sessionDate)) {
+                JOptionPane.showMessageDialog(this, INVALID_SESSION_DATE_MESSAGE, INVALID_SESSION_DATE_TITLE, JOptionPane.ERROR_MESSAGE);
+            } else {
+                add(addContent(new JPanel(new GridBagLayout())), BorderLayout.CENTER);
             }
         }
 
-        if (null != modalityLabel) {
-            if (modalityLabel instanceof DicomSessionVariable) {
-                final DicomSessionVariable dsv = (DicomSessionVariable)modalityLabel;
-                try {
-                    
-                    logger.trace("setting (hidden) modality label from lead modality {}", leadModality);
-                    dsv.setInitialValue(new org.nrg.dcm.edit.ConstantValue(leadModality));
-                    dsv.setIsHidden(true);
-                } catch (MultipleInitializationException exception) {
-                    logger.debug("Got MultipleInitializationException", exception);
-                }
-            } else if (modalityLabel instanceof EcatSessionVariable) {
-                final EcatSessionVariable esv = (EcatSessionVariable)modalityLabel;
-                try {
-                    esv.setInitialValue(new org.nrg.ecat.edit.ConstantValue(leadModality));
-                    esv.setIsHidden(true);
-                } catch (org.nrg.ecat.edit.MultipleInitializationException exception) {
-                    logger.debug("Got MultipleInitializationException", exception);
-                }
-            } else {
-                logger.error("unexpected variable type for modality label {}", modalityLabel);
+        /*
+         * (non-Javadoc)
+         * @see org.netbeans.spi.wizard.WizardPage#validateContents(java.awt.Component, java.lang.Object)
+         */
+        @Override
+        protected String validateContents(final Component component, final Object event) {
+            if (!isSessionDateOk(confirmedDate, sessionDate)) {
+                // We have to go through this whole rigamarole to deal with the Java Date class's inadequacies and general crappiness.
+                // The year being set by dcm4che seems to absolute, i.e. setting 2012 when it really means 2012. The nerve. Instead,
+                // according to Java, it should be setting the offset from 1900, i.e. 112, when it means 2012. But we get what we get.
+                // So, check for a year greater than 1900 and, if found, offset by -1900 and hope like crazy that that's the right thing.
+                Calendar calendar = Calendar.getInstance();
+                calendar.setTime(confirmedDate);
+                //            int year = calendar.get(Calendar.YEAR);
+                //            if (year > 1900) {
+                //                calendar.set(Calendar.YEAR, year - 1900);
+                //            }
+                return "Click the Previous button and select a session with scan date of " +  FORMATTER.format(calendar.getTime());
             }
 
+            // If verifyDate is empty, we won't allow user to proceed, but we won't do any other validation.
+            boolean stopForDuplicateSessionId = stopForDuplicateSessionId();
+            if (stopForDuplicateSessionId) {
+                return "You must either select another session, change the session ID, or indicate how you want to handle the duplicate session ID for your auto-archiving project.";
+            }
+            final SortedSet<String> names = Sets.newTreeSet();
+            for (final SessionVariable v : invalid) {
+                names.add(v.getName());
+            }
+            if (names.size() > 0) {
+                final StringBuilder buffer = new StringBuilder("Some fields have invalid values: ");
+                final Joiner joiner = Joiner.on(", ");
+                return joiner.appendTo(buffer, names).toString();
+            }
+            return null;
         }
 
-        final ExcludingValueValidator excludeExistingSessions = getSessionExcluder(project, true);
+        /**
+         * Initializes all incoming data from the wizard's data map.
+         */
+        private void initialize() {
+            project = (Project) getWizardData(SelectProjectPage.PRODUCT_NAME);
+            subject = (Subject) getWizardData(SelectSubjectPage.PRODUCT_NAME);
+            session = (Session) getWizardData(SelectSessionPage.PRODUCT_NAME);
+            sessionDate = session.getDateTime();
+            sessionLabel = (SessionVariable) getWizardData(SessionVariableNames.SESSION_LABEL);
+            Object candidate = getWizardData(ConfirmSessionDatePage.PRODUCT_NAME);
+            // If we got a candidate for the confirmed date...
+            if (candidate != null) {
+                // See if it's a date.
+                if (candidate instanceof Date) {
+                    // If so, stash that.
+                    confirmedDate = (Date) candidate;
+                }
+                // If it wasn't a date, check if it's a string and is the no_session_date qualifier.
+                else if (candidate instanceof String && "no_session_date".equals(candidate)) {
+                    // So if it's no session date, there's a good chance the session date is null. If so, initialize both
+                    // of the dates so that they're matching and we can pass through the date-check logic cleanly.
+                    if (sessionDate == null) {
+                        sessionDate = new Date();
+                    }
+                    confirmedDate = sessionDate;
+                }
+            }
+        }
 
-        logger.trace("starting upload for {}", predefs);
+        private JPanel addContent(final JPanel panel) {
+            logger.trace("adding content");
 
-        // If no initial value was provided for session, construct one in the format
-        // {subject}_{modality}{index}, where index is the smallest positive integer
-        // that results in a session label not already defined in this project.
-        final IndexedDependentSessionVariable defaultSessionLabel;
+            try {
+                isAutoArchiving = project.getPrearchiveCode() != PrearchiveCode.Manual;
+            } catch (Exception exception) {
+                logger.warn("Error when retrieving project prearchive code", exception);
+            }
 
-        logger.trace("building indexed modality default session label from {}", leadModality);
-        defaultSessionLabel = new IndexedDependentSessionVariable(SESSION_LABEL,
-                                                                      predefs.get(SUBJECT), "%s_" + leadModality + "%d", excludeExistingSessions);
+            final Map<String,SessionVariable> predefs = Maps.newLinkedHashMap();
+            predefs.put(PROJECT, new AssignedSessionVariable(PROJECT, project.toString()).fixValue());
+            predefs.put(SUBJECT, new AssignedSessionVariable(SUBJECT, subject.toString()).fixValue());
+            SessionVariable vSession = (SessionVariable) getWizardData(PREDEF_SESSION);
+            if (null != vSession) {
+                predefs.put(SESSION_LABEL, vSession);
+            }
 
-        if (null == vSession) {
-            vars.addFirst(vSession = defaultSessionLabel);
-        } else {
-            if (vSession.isMutable() &&
-                    !LabelValueValidator.getInstance().isValid(vSession.getValue())) {
-                // The existing session variable doesn't have a useful initial value,
-                // so shadow it with the default label format.
-                final SessionVariable scriptSession = vSession;
-                try {
-                    vSession.setValue(defaultSessionLabel.getValue());
-                } catch (InvalidValueException e) {
-                    logger.error("unable to set shadowed session variable", e);
+            final LinkedList<SessionVariable> vars = Lists.newLinkedList(session.getVariables(getWizardDataMap()));
+            putWizardData(PRODUCT_NAME, vars);
+            logger.trace("initialized session variables: {}",  vars);
+
+            final Set<String> modalities = session.getModalities();
+            final String leadModality = modalities.contains("PET") ? "PET" : SOPModel.getLeadModality(modalities);
+            // modality = PET indicates an ECAT dataset, so obviously that's PET.
+
+            // Strip project and subject out of the List: these are immutable and we'll
+            // display them separately.  Also check whether session has been defined;
+            // we'll use the existing variable if so, or add a new one if not.
+            SessionVariable modalityLabel = null;
+            for (final ListIterator<SessionVariable> i = vars.listIterator(); i.hasNext(); ) {
+                final SessionVariable v = i.next();
+                final String name = v.getName();
+                if (SESSION_LABEL.equals(name)) {
+                    vSession = v;
+                } // no else clause: session might also be a predef and we want normal predef handling
+
+                if (predefs.containsKey(name)) {
+                    final SessionVariable predef = predefs.get(name);
+                    logger.trace("found predefined variable {} in script", v);
+                    i.remove();
+                    try {
+                        v.fixValue(predef.getValue());
+                    } catch (InvalidValueException e) {
+                        throw new RuntimeException(e);
+                    }
+                    predefs.put(v.getName(), v);
+                } else if (MODALITY_LABEL.equals(name)) {
+                    modalityLabel = v;   // Process this later
                 }
+            }
 
-                defaultSessionLabel.addShadow(vSession);
-                vSession = defaultSessionLabel;
-                for (final ListIterator<SessionVariable> li = vars.listIterator(); li.hasNext(); ) {
-                    if (scriptSession == li.next()) {
-                        li.set(vSession);
-                        break;
+            if (null != modalityLabel) {
+                if (modalityLabel instanceof DicomSessionVariable) {
+                    final DicomSessionVariable dsv = (DicomSessionVariable)modalityLabel;
+                    try {                    
+                        logger.trace("setting (hidden) modality label from lead modality {}", leadModality);
+                        dsv.setInitialValue(new org.nrg.dcm.edit.ConstantValue(leadModality));
+                        dsv.setIsHidden(true);
+                    } catch (MultipleInitializationException exception) {
+                        logger.debug("Got MultipleInitializationException", exception);
+                    }
+                } else if (modalityLabel instanceof EcatSessionVariable) {
+                    final EcatSessionVariable esv = (EcatSessionVariable)modalityLabel;
+                    try {
+                        esv.setInitialValue(new org.nrg.ecat.edit.ConstantValue("PET"));
+                        esv.setIsHidden(true);
+                    } catch (org.nrg.ecat.edit.MultipleInitializationException exception) {
+                        logger.debug("Got MultipleInitializationException", exception);
                     }
                 }
             }
-        }
-        if (vSession.isMutable()) {
 
-            vSession.addValidator(LabelValueValidator.getInstance());
-            // vSession.addValidator(excludeExistingSessions);
+            final ExcludingValueValidator excludeExistingSessions = getSessionExcluder(project, true);
+
+            logger.trace("starting upload for {}", predefs);
+
+            // If no initial value was provided for session, construct one in the format
+            // {subject}_{modality}{index}, where index is the smallest positive integer
+            // that results in a session label not already defined in this project.
+            final IndexedDependentSessionVariable defaultSessionLabel;
+
+            logger.trace("building indexed modality default session label from {}", leadModality);
+            defaultSessionLabel = new IndexedDependentSessionVariable(SESSION_LABEL,
+                    predefs.get(SUBJECT), "%s_" + leadModality + "%d", excludeExistingSessions);
+
+            if (null == vSession) {
+                vars.addFirst(vSession = defaultSessionLabel);
+            } else {
+                if (vSession.isMutable() &&
+                        !LabelValueValidator.getInstance().isValid(vSession.getValue())) {
+                    // The existing session variable doesn't have a useful initial value,
+                    // so shadow it with the default label format.
+                    final SessionVariable scriptSession = vSession;
+                    try {
+                        vSession.setValue(defaultSessionLabel.getValue());
+                    } catch (InvalidValueException e) {
+                        logger.error("unable to set shadowed session variable", e);
+                    }
+
+                    defaultSessionLabel.addShadow(vSession);
+                    vSession = defaultSessionLabel;
+                    for (final ListIterator<SessionVariable> li = vars.listIterator(); li.hasNext(); ) {
+                        if (scriptSession == li.next()) {
+                            li.set(vSession);
+                            break;
+                        }
+                    }
+                }
+            }
+            if (vSession.isMutable()) {
+
+                vSession.addValidator(LabelValueValidator.getInstance());
+                // vSession.addValidator(excludeExistingSessions);
+            }
+
+            // Session name, like project and subject, must be available in the wizard params.
+            putWizardData(vSession.getName(), vSession);
+
+            addSessionIdentifiers(panel, predefs, vars);
+
+            return panel;
         }
 
-        // Session name, like project and subject, must be available in the wizard params.
-        putWizardData(vSession.getName(), vSession);
-
-        addSessionIdentifiers(panel, predefs, vars);
-
-        return panel;
-    }
-
-    /**
-     * Indicates whether a duplicate session ID has been identified for an auto-archived project and if the user has
-     * indicated how to handle it.
-     * @return <b>false</b> if the project is not set to auto-archive, the selected session ID is not a duplicate of an
-     *         existing session ID, or the user has indicated whether the duplicate session should be appended to or
-     *         overwrite the existing session with the duplicate ID. In the case where a duplicate session ID is found
-     *         for an auto-archiving project and the user selects <b>Cancel</b> on the option dialog, this method
-     *         returns <b>true</b>.
-     */
-    private boolean stopForDuplicateSessionId() {
-        // We only care about duplicate session IDs when the project is auto-archived.
-        if (isAutoArchiving) {
-            try {
-                Map<String, String> labels = project.getSessionLabels();
-                if (labels.containsKey(sessionLabel.getValue())) {
-                    int selected = JOptionPane.showOptionDialog(this, DUP_SESSION_ID_MESSAGE, DUP_SESSION_ID_TITLE, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, DUP_SESSION_ID_OPTIONS, DUP_SESSION_ID_OPTIONS[2]);
-                    switch (selected) {
+        /**
+         * Indicates whether a duplicate session ID has been identified for an auto-archived project and if the user has
+         * indicated how to handle it.
+         * @return <b>false</b> if the project is not set to auto-archive, the selected session ID is not a duplicate of an
+         *         existing session ID, or the user has indicated whether the duplicate session should be appended to or
+         *         overwrite the existing session with the duplicate ID. In the case where a duplicate session ID is found
+         *         for an auto-archiving project and the user selects <b>Cancel</b> on the option dialog, this method
+         *         returns <b>true</b>.
+         */
+        private boolean stopForDuplicateSessionId() {
+            // We only care about duplicate session IDs when the project is auto-archived.
+            if (isAutoArchiving) {
+                try {
+                    Map<String, String> labels = project.getSessionLabels();
+                    if (labels.containsKey(sessionLabel.getValue())) {
+                        int selected = JOptionPane.showOptionDialog(this, DUP_SESSION_ID_MESSAGE, DUP_SESSION_ID_TITLE, JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, DUP_SESSION_ID_OPTIONS, DUP_SESSION_ID_OPTIONS[2]);
+                        switch (selected) {
                         case JOptionPane.YES_OPTION:
                             putWizardData(Project.AUTO_ARCHIVE, AutoArchive.Overwrite);
                             break;
                             break;
                         case JOptionPane.CANCEL_OPTION:
                             return true;
+                        }
                     }
+                } catch (Exception exception) {
+                    logger.warn("Error when retrieving project session labels for project", exception);
                 }
-            } catch (Exception exception) {
-                logger.warn("Error when retrieving project session labels for project", exception);
             }
+
+            return false;
         }
 
-        return false;
-    }
+        private void addSessionIdentifiers(JPanel panel, Map<String, SessionVariable> predefs, LinkedList<SessionVariable> vars) {
+            for (final SessionVariable v : predefs.values()) {
+                panel.add(new JLabel(v.getDescription()), VariableAssignmentManager.labelConstraint);
+                panel.add(new JLabel(v.getValue()), VariableAssignmentManager.valueConstraint);
+            }
 
-    private void addSessionIdentifiers(JPanel panel, Map<String, SessionVariable> predefs, LinkedList<SessionVariable> vars) {
-        for (final SessionVariable v : predefs.values()) {
-            panel.add(new JLabel(v.getDescription()), VariableAssignmentManager.labelConstraint);
-            panel.add(new JLabel(v.getValue()), VariableAssignmentManager.valueConstraint);
+            if(vars.size() > 0){
+                panel.add(new JLabel("<html><b>Set session identifiers:</b></html>"), SPANNING);
+                panel.add(new JLabel(), VariableAssignmentManager.messageConstraint);
+            }
+            new VariableAssignmentManager(panel, vars, this);
         }
 
-        if(vars.size() > 0){
-        	panel.add(new JLabel("<html><b>Set session identifiers:</b></html>"), SPANNING);
-        	panel.add(new JLabel(), VariableAssignmentManager.messageConstraint);
+        private ExcludingValueValidator getSessionExcluder(final Project project, final boolean isStrict) {
+            Set<String> labels;
+            try {
+                labels = project.getSessionLabels().keySet();
+            } catch (Exception e) {
+                labels = Collections.emptySet();
+            }
+            return new ExcludingValueValidator(labels, "Project already contains a session named %s.", isStrict);
         }
-        new VariableAssignmentManager(panel, vars, this);
-    }
 
-    private ExcludingValueValidator getSessionExcluder(final Project project, final boolean isStrict) {
-        Set<String> labels;
-        try {
-            labels = project.getSessionLabels().keySet();
-        } catch (Exception e) {
-            labels = Collections.emptySet();
-        }
-        return new ExcludingValueValidator(labels, "Project already contains a session named %s.", isStrict);
-    }
-
-    private static final DateFormat FORMATTER = new SimpleDateFormat("M/d/yyyy HH:mm");
+        private static final DateFormat FORMATTER = new SimpleDateFormat("M/d/yyyy HH:mm");
 }