extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/plugins/TransportBoxImporter.java

Code
Comments
Other
Rev Date Author Line
7139 27 Apr 23 nicklas 1 package net.sf.basedb.reggie.plugins;
7139 27 Apr 23 nicklas 2
7139 27 Apr 23 nicklas 3 import java.io.IOException;
7139 27 Apr 23 nicklas 4 import java.io.InputStream;
7139 27 Apr 23 nicklas 5 import java.util.ArrayList;
7191 23 May 23 nicklas 6 import java.util.Collection;
7139 27 Apr 23 nicklas 7 import java.util.HashMap;
7191 23 May 23 nicklas 8 import java.util.LinkedHashMap;
7139 27 Apr 23 nicklas 9 import java.util.List;
7155 10 May 23 nicklas 10 import java.util.Locale;
7139 27 Apr 23 nicklas 11 import java.util.Map;
7139 27 Apr 23 nicklas 12 import java.util.regex.Pattern;
7139 27 Apr 23 nicklas 13
7141 02 May 23 nicklas 14 import org.json.simple.JSONArray;
7139 27 Apr 23 nicklas 15 import org.json.simple.JSONObject;
7139 27 Apr 23 nicklas 16
7158 10 May 23 nicklas 17 import net.sf.basedb.core.AnnotationRestriction;
7158 10 May 23 nicklas 18 import net.sf.basedb.core.AnnotationSimpleRestriction;
7185 22 May 23 nicklas 19 import net.sf.basedb.core.AnyToAny;
7142 04 May 23 nicklas 20 import net.sf.basedb.core.BioMaterialEvent;
7141 02 May 23 nicklas 21 import net.sf.basedb.core.BioPlate;
7140 28 Apr 23 nicklas 22 import net.sf.basedb.core.BioWell;
7139 27 Apr 23 nicklas 23 import net.sf.basedb.core.DbControl;
7141 02 May 23 nicklas 24 import net.sf.basedb.core.Extract;
7185 22 May 23 nicklas 25 import net.sf.basedb.core.File;
7161 11 May 23 nicklas 26 import net.sf.basedb.core.InvalidDataException;
7142 04 May 23 nicklas 27 import net.sf.basedb.core.ItemList;
7141 02 May 23 nicklas 28 import net.sf.basedb.core.ItemQuery;
7158 10 May 23 nicklas 29 import net.sf.basedb.core.ItemSubtype;
7141 02 May 23 nicklas 30 import net.sf.basedb.core.MeasuredBioMaterial;
7158 10 May 23 nicklas 31 import net.sf.basedb.core.Operator;
7162 12 May 23 nicklas 32 import net.sf.basedb.core.ProgressReporter;
7141 02 May 23 nicklas 33 import net.sf.basedb.core.Sample;
7168 12 May 23 nicklas 34 import net.sf.basedb.core.StringUtil;
7141 02 May 23 nicklas 35 import net.sf.basedb.core.query.Annotations;
7141 02 May 23 nicklas 36 import net.sf.basedb.core.query.Expressions;
7141 02 May 23 nicklas 37 import net.sf.basedb.core.query.Hql;
7141 02 May 23 nicklas 38 import net.sf.basedb.core.query.Restrictions;
7140 28 Apr 23 nicklas 39 import net.sf.basedb.reggie.JsonUtil;
7141 02 May 23 nicklas 40 import net.sf.basedb.reggie.Reggie;
7251 08 Jun 23 nicklas 41 import net.sf.basedb.reggie.Site;
7141 02 May 23 nicklas 42 import net.sf.basedb.reggie.dao.Annotationtype;
7164 12 May 23 nicklas 43 import net.sf.basedb.reggie.dao.BiomaterialList;
7143 05 May 23 nicklas 44 import net.sf.basedb.reggie.dao.Blood;
7142 04 May 23 nicklas 45 import net.sf.basedb.reggie.dao.Case;
7144 05 May 23 nicklas 46 import net.sf.basedb.reggie.dao.Consent;
7142 04 May 23 nicklas 47 import net.sf.basedb.reggie.dao.Dna;
7142 04 May 23 nicklas 48 import net.sf.basedb.reggie.dao.FlowThrough;
7142 04 May 23 nicklas 49 import net.sf.basedb.reggie.dao.Lysate;
7141 02 May 23 nicklas 50 import net.sf.basedb.reggie.dao.NoSpecimen;
7142 04 May 23 nicklas 51 import net.sf.basedb.reggie.dao.Patient;
7142 04 May 23 nicklas 52 import net.sf.basedb.reggie.dao.ReggieItem;
7142 04 May 23 nicklas 53 import net.sf.basedb.reggie.dao.Rna;
7140 28 Apr 23 nicklas 54 import net.sf.basedb.reggie.dao.SpecimenTube;
7140 28 Apr 23 nicklas 55 import net.sf.basedb.reggie.dao.StoragePlate;
7141 02 May 23 nicklas 56 import net.sf.basedb.reggie.dao.Subtype;
7193 23 May 23 nicklas 57 import net.sf.basedb.util.EqualsHelper;
7185 22 May 23 nicklas 58 import net.sf.basedb.util.FileUtil;
7161 11 May 23 nicklas 59 import net.sf.basedb.util.Values;
7139 27 Apr 23 nicklas 60 import net.sf.basedb.util.excel.XlsxToCsvUtil;
7139 27 Apr 23 nicklas 61 import net.sf.basedb.util.parser.ConstantMapper;
7139 27 Apr 23 nicklas 62 import net.sf.basedb.util.parser.FlatFileParser;
7139 27 Apr 23 nicklas 63 import net.sf.basedb.util.parser.Mapper;
7139 27 Apr 23 nicklas 64 import net.sf.basedb.util.parser.FlatFileParser.LineType;
7139 27 Apr 23 nicklas 65
7139 27 Apr 23 nicklas 66 /**
7139 27 Apr 23 nicklas 67   Importer for the sample sheet with information about
7139 27 Apr 23 nicklas 68   transport boxes from CMD.
7139 27 Apr 23 nicklas 69   @since 4.47
7139 27 Apr 23 nicklas 70 */
7139 27 Apr 23 nicklas 71 public class TransportBoxImporter 
7139 27 Apr 23 nicklas 72 {
7142 04 May 23 nicklas 73   /**
7142 04 May 23 nicklas 74     This ID is used on work lists for transport boxes. 
7142 04 May 23 nicklas 75   */
7142 04 May 23 nicklas 76   public static final String WORK_LIST_ID = "net.sf.basedb.reggie.transport-box";
7139 27 Apr 23 nicklas 77
7142 04 May 23 nicklas 78   private final List<String> messages;
7139 27 Apr 23 nicklas 79   private final List<String> errorMessages;
7144 05 May 23 nicklas 80   private final List<String> debugMessages;
7139 27 Apr 23 nicklas 81
7139 27 Apr 23 nicklas 82   private final List<SampleEntry> specimen;
7139 27 Apr 23 nicklas 83   private final Map<String, SampleEntry> specimenByClarityId;
7191 23 May 23 nicklas 84   private final Map<String, Box> boxes;
7139 27 Apr 23 nicklas 85   
7139 27 Apr 23 nicklas 86   private List<String> worksheets;
7139 27 Apr 23 nicklas 87   private String parsedWorksheet;
7141 02 May 23 nicklas 88   private int totalErrors;
7141 02 May 23 nicklas 89   private int totalWarnings;
7139 27 Apr 23 nicklas 90   
7143 05 May 23 nicklas 91   private int numMappedToSpecimen;
7143 05 May 23 nicklas 92   private int numMappedToNoSpecimen;
7143 05 May 23 nicklas 93   private int numMappedToCase;
7143 05 May 23 nicklas 94   private int numMappedToBlood;
7143 05 May 23 nicklas 95   private int numMappedToNothing;
7143 05 May 23 nicklas 96   private int numNoConsent;
7143 05 May 23 nicklas 97   
7191 23 May 23 nicklas 98   private JSONObject requestParameters;
7161 11 May 23 nicklas 99   
7139 27 Apr 23 nicklas 100   public TransportBoxImporter()
7139 27 Apr 23 nicklas 101   {
7142 04 May 23 nicklas 102     this.messages = new ArrayList<>();
7142 04 May 23 nicklas 103     this.errorMessages = new ArrayList<>();
7144 05 May 23 nicklas 104     this.debugMessages = new ArrayList<>();
7139 27 Apr 23 nicklas 105     this.specimen = new ArrayList<>();
7139 27 Apr 23 nicklas 106     this.specimenByClarityId = new HashMap<>();
7191 23 May 23 nicklas 107     this.boxes = new LinkedHashMap<>();
7139 27 Apr 23 nicklas 108   }
7139 27 Apr 23 nicklas 109
7139 27 Apr 23 nicklas 110   public boolean hasWarning()
7139 27 Apr 23 nicklas 111   {
7141 02 May 23 nicklas 112     return totalWarnings > 0;
7139 27 Apr 23 nicklas 113   }
7139 27 Apr 23 nicklas 114   
7141 02 May 23 nicklas 115   public int getTotalWarnings()
7141 02 May 23 nicklas 116   {
7141 02 May 23 nicklas 117     return totalWarnings;
7141 02 May 23 nicklas 118   }
7141 02 May 23 nicklas 119   
7139 27 Apr 23 nicklas 120   public boolean hasError()
7139 27 Apr 23 nicklas 121   {
7141 02 May 23 nicklas 122     return totalErrors > 0;
7139 27 Apr 23 nicklas 123   }
7139 27 Apr 23 nicklas 124   
7141 02 May 23 nicklas 125   public int getTotalErrors()
7141 02 May 23 nicklas 126   {
7141 02 May 23 nicklas 127     return totalErrors;
7141 02 May 23 nicklas 128   }
7141 02 May 23 nicklas 129   
7142 04 May 23 nicklas 130   public List<String> getMessages()
7142 04 May 23 nicklas 131   {
7142 04 May 23 nicklas 132     return messages;
7142 04 May 23 nicklas 133   }
7142 04 May 23 nicklas 134   
7139 27 Apr 23 nicklas 135   public List<String> getErrorMessages()
7139 27 Apr 23 nicklas 136   {
7139 27 Apr 23 nicklas 137     return errorMessages;
7139 27 Apr 23 nicklas 138   }
7141 02 May 23 nicklas 139   
7144 05 May 23 nicklas 140   public List<String> getDebugMessages()
7144 05 May 23 nicklas 141   {
7144 05 May 23 nicklas 142     return debugMessages;
7144 05 May 23 nicklas 143   }
7144 05 May 23 nicklas 144   
7144 05 May 23 nicklas 145   public void addDebugMessage(String msg)
7144 05 May 23 nicklas 146   {
7144 05 May 23 nicklas 147     this.messages.add("[Debug] " + msg);
7144 05 May 23 nicklas 148   }
7144 05 May 23 nicklas 149
7139 27 Apr 23 nicklas 150   public void addWarningMessage(String msg)
7139 27 Apr 23 nicklas 151   {
7142 04 May 23 nicklas 152     this.messages.add("[Warning] " + msg);
7141 02 May 23 nicklas 153     this.totalWarnings++;
7139 27 Apr 23 nicklas 154   }
7139 27 Apr 23 nicklas 155   
7139 27 Apr 23 nicklas 156   public void addErrorMessage(String msg)
7139 27 Apr 23 nicklas 157   {
7142 04 May 23 nicklas 158     msg = "[Error] " + msg;
7142 04 May 23 nicklas 159     this.messages.add(msg);
7142 04 May 23 nicklas 160     this.errorMessages.add(msg);
7141 02 May 23 nicklas 161     this.totalErrors++;
7139 27 Apr 23 nicklas 162   }
7139 27 Apr 23 nicklas 163   
7142 04 May 23 nicklas 164   public void addImportMessage(String msg)
7142 04 May 23 nicklas 165   {
7142 04 May 23 nicklas 166     this.messages.add(msg);
7142 04 May 23 nicklas 167   }
7142 04 May 23 nicklas 168   
7143 05 May 23 nicklas 169   public int getNumSamples()
7139 27 Apr 23 nicklas 170   {
7139 27 Apr 23 nicklas 171     return specimen.size();
7139 27 Apr 23 nicklas 172   }
7139 27 Apr 23 nicklas 173   
7139 27 Apr 23 nicklas 174   public int getNumBoxes()
7139 27 Apr 23 nicklas 175   {
7139 27 Apr 23 nicklas 176     return boxes.size();
7139 27 Apr 23 nicklas 177   }
7139 27 Apr 23 nicklas 178   
7139 27 Apr 23 nicklas 179   public List<SampleEntry> getSpecimen()
7139 27 Apr 23 nicklas 180   {
7139 27 Apr 23 nicklas 181     return specimen;
7139 27 Apr 23 nicklas 182   }
7139 27 Apr 23 nicklas 183   
7191 23 May 23 nicklas 184   public Collection<Box> getBoxes()
7139 27 Apr 23 nicklas 185   {
7191 23 May 23 nicklas 186     return boxes.values();
7139 27 Apr 23 nicklas 187   }
7139 27 Apr 23 nicklas 188   
7139 27 Apr 23 nicklas 189   public List<String> getWorksheets()
7139 27 Apr 23 nicklas 190   {
7139 27 Apr 23 nicklas 191     return worksheets;
7139 27 Apr 23 nicklas 192   }
7139 27 Apr 23 nicklas 193   
7139 27 Apr 23 nicklas 194   public String getParsedWorksheet()
7139 27 Apr 23 nicklas 195   {
7139 27 Apr 23 nicklas 196     return parsedWorksheet;
7139 27 Apr 23 nicklas 197   }
7139 27 Apr 23 nicklas 198
7143 05 May 23 nicklas 199   public int getMappedToSpecimen()
7143 05 May 23 nicklas 200   {
7143 05 May 23 nicklas 201     return numMappedToSpecimen;
7143 05 May 23 nicklas 202   }
7143 05 May 23 nicklas 203   
7143 05 May 23 nicklas 204   public int getMappedToNoSpecimen()
7143 05 May 23 nicklas 205   {
7143 05 May 23 nicklas 206     return numMappedToNoSpecimen;
7143 05 May 23 nicklas 207   }
7143 05 May 23 nicklas 208   
7143 05 May 23 nicklas 209   public int getMappedToCase()
7143 05 May 23 nicklas 210   {
7143 05 May 23 nicklas 211     return numMappedToCase;
7143 05 May 23 nicklas 212   }
7143 05 May 23 nicklas 213   
7143 05 May 23 nicklas 214   public int getMappedToBlood()
7143 05 May 23 nicklas 215   {
7143 05 May 23 nicklas 216     return numMappedToBlood;
7143 05 May 23 nicklas 217   }
7143 05 May 23 nicklas 218   
7143 05 May 23 nicklas 219   public int getMappedToNothing()
7143 05 May 23 nicklas 220   {
7143 05 May 23 nicklas 221     return numMappedToNothing;
7143 05 May 23 nicklas 222   }
7143 05 May 23 nicklas 223   
7143 05 May 23 nicklas 224   public int getNumNoConsent()
7143 05 May 23 nicklas 225   {
7143 05 May 23 nicklas 226     return numNoConsent;
7143 05 May 23 nicklas 227   }
7143 05 May 23 nicklas 228   
7139 27 Apr 23 nicklas 229   /**
7161 11 May 23 nicklas 230     Set parameters that was entered on the import form in the web client.
7161 11 May 23 nicklas 231     The array should have the same number of elements as there are rows
7161 11 May 23 nicklas 232     in the imported file.
7161 11 May 23 nicklas 233   */
7191 23 May 23 nicklas 234   public void setRequestParameters(JSONObject json)
7161 11 May 23 nicklas 235   {
7161 11 May 23 nicklas 236     this.requestParameters = json;
7161 11 May 23 nicklas 237   }
7161 11 May 23 nicklas 238   
7161 11 May 23 nicklas 239   /**
7139 27 Apr 23 nicklas 240     Import data from the input stream.
7139 27 Apr 23 nicklas 241     @param in
7139 27 Apr 23 nicklas 242   */
7185 22 May 23 nicklas 243   public boolean doImport(DbControl dc, File importFile, String workSheet, boolean validateOnly, ProgressReporter progress)
7139 27 Apr 23 nicklas 244     throws IOException
7139 27 Apr 23 nicklas 245   {
7161 11 May 23 nicklas 246     if (!validateOnly && requestParameters == null)
7161 11 May 23 nicklas 247     {
7161 11 May 23 nicklas 248       throw new IllegalStateException("Must set 'requestParameters' before importing");
7161 11 May 23 nicklas 249     }
7161 11 May 23 nicklas 250     
7139 27 Apr 23 nicklas 251     specimen.clear();
7139 27 Apr 23 nicklas 252     specimenByClarityId.clear();
7139 27 Apr 23 nicklas 253     boxes.clear();
7142 04 May 23 nicklas 254     messages.clear();
7139 27 Apr 23 nicklas 255     worksheets = null;
7139 27 Apr 23 nicklas 256     parsedWorksheet = null;
7141 02 May 23 nicklas 257     totalErrors = 0;
7141 02 May 23 nicklas 258     totalWarnings = 0;
7143 05 May 23 nicklas 259     numMappedToSpecimen = 0;
7143 05 May 23 nicklas 260     numMappedToNoSpecimen = 0;
7143 05 May 23 nicklas 261     numMappedToCase = 0;
7143 05 May 23 nicklas 262     numMappedToBlood = 0;
7143 05 May 23 nicklas 263     numMappedToNothing = 0;
7143 05 May 23 nicklas 264     numNoConsent = 0;
7143 05 May 23 nicklas 265
7185 22 May 23 nicklas 266     InputStream in = null;
7185 22 May 23 nicklas 267     try
7139 27 Apr 23 nicklas 268     {
7185 22 May 23 nicklas 269       String fileName = importFile.getName();
7185 22 May 23 nicklas 270       in = importFile.getDownloadStream(0);
7185 22 May 23 nicklas 271       if (progress != null) progress.display(5, "Parsing "+fileName+"...");
7185 22 May 23 nicklas 272       
7185 22 May 23 nicklas 273       FlatFileParser ffp = new FlatFileParser();
7185 22 May 23 nicklas 274       ffp.setExcelSheet(workSheet);
7185 22 May 23 nicklas 275       ffp.setDataHeaderRegexp(Pattern.compile(".*\\bClarity ID\\b.*"));
7185 22 May 23 nicklas 276       ffp.setDataSplitterRegexp(Pattern.compile("\t")); // Split on tab
7185 22 May 23 nicklas 277       ffp.setIgnoreRegexp(Pattern.compile("\\s*")); // Ignore lines with only white-space
7185 22 May 23 nicklas 278       ffp.setUseNullIfEmpty(true);
7185 22 May 23 nicklas 279       ffp.setTrimWhiteSpace(true);
7185 22 May 23 nicklas 280       
7185 22 May 23 nicklas 281       ffp.setInputStream(in, "UTF-8");
7185 22 May 23 nicklas 282       
7185 22 May 23 nicklas 283       XlsxToCsvUtil workbook = ffp.getCurrentExcelWorkbook();
7185 22 May 23 nicklas 284       if (workbook != null)
7162 12 May 23 nicklas 285       {
7185 22 May 23 nicklas 286         worksheets = workbook.getSheetNames();
7185 22 May 23 nicklas 287         parsedWorksheet = ffp.getExcelSheet();
7185 22 May 23 nicklas 288         fileName += "/" + parsedWorksheet;
7162 12 May 23 nicklas 289       }
7185 22 May 23 nicklas 290   
7185 22 May 23 nicklas 291       LineType headerLine = ffp.parseHeaders();
7185 22 May 23 nicklas 292       int lineNo = ffp.getParsedLines();
7185 22 May 23 nicklas 293       if (headerLine != LineType.DATA_HEADER)
7142 04 May 23 nicklas 294       {
7185 22 May 23 nicklas 295         addErrorMessage("File '" + fileName + "' line 1−" + lineNo + ": Could not find header line with 'Clarity ID' column.");
7185 22 May 23 nicklas 296         return false; // Can't continue if no data is found in the file
7185 22 May 23 nicklas 297       }
7185 22 May 23 nicklas 298       ffp.setIgnoreNonExistingColumns(true);
7185 22 May 23 nicklas 299       
7185 22 May 23 nicklas 300       String fileAndLine = "File '" + fileName + "' line " + lineNo + ": ";
7185 22 May 23 nicklas 301       Mapper clarityIdMapper = getRequiredMapper(ffp, "Clarity ID", fileAndLine);
7185 22 May 23 nicklas 302       Mapper scanbIdMapper = getRequiredMapper(ffp, "SCAN-B nummer", fileAndLine);
7185 22 May 23 nicklas 303       Mapper boxMapper = getRequiredMapper(ffp, "Förvaring", fileAndLine);
7185 22 May 23 nicklas 304       if (hasError()) return false;
7185 22 May 23 nicklas 305       
7185 22 May 23 nicklas 306       Mapper padMapper = getOptionalMapper(ffp, "PADRemiss", fileAndLine);
7185 22 May 23 nicklas 307       // Parse the complete file before validation so that we can use the progress reporter
7185 22 May 23 nicklas 308       while (ffp.hasMoreData())
7185 22 May 23 nicklas 309       {
7185 22 May 23 nicklas 310         FlatFileParser.Data data = ffp.nextData();
7185 22 May 23 nicklas 311         lineNo = ffp.getParsedLines();
7185 22 May 23 nicklas 312         fileAndLine = "File '" + fileName + "' line " + lineNo + ": ";
7185 22 May 23 nicklas 313   
7185 22 May 23 nicklas 314         SampleEntry s = new SampleEntry(data.line(), fileAndLine, lineNo);
7185 22 May 23 nicklas 315         specimen.add(s);
7185 22 May 23 nicklas 316         s.clarityId = clarityIdMapper.getString(data);
7185 22 May 23 nicklas 317         s.scanBId = scanbIdMapper.getString(data);
7234 02 Jun 23 nicklas 318         if (s.scanBId != null && s.scanBId.endsWith("T"))
7234 02 Jun 23 nicklas 319         {
7234 02 Jun 23 nicklas 320           // Suffix 'T' is for the frozen piece and we should normally not see it
7234 02 Jun 23 nicklas 321           // but if it is here we simple remove it
7246 07 Jun 23 nicklas 322           s.scanBId = s.scanBId.substring(0, s.scanBId.length()-1);
7234 02 Jun 23 nicklas 323         }
7185 22 May 23 nicklas 324         s.transportBox = boxMapper.getString(data);
7185 22 May 23 nicklas 325         s.pad = padMapper.getString(data);
7185 22 May 23 nicklas 326         if (s.pad != null) s.pad = s.pad.toUpperCase(Locale.ROOT);
7191 23 May 23 nicklas 327         if (s.transportBox != null) 
7191 23 May 23 nicklas 328         {
7191 23 May 23 nicklas 329           Box b = boxes.get(s.transportBox);
7191 23 May 23 nicklas 330           if (b == null) 
7191 23 May 23 nicklas 331           {
7191 23 May 23 nicklas 332             b = new Box(s.transportBox);
7191 23 May 23 nicklas 333             boxes.put(s.transportBox, b);
7191 23 May 23 nicklas 334           }
7191 23 May 23 nicklas 335           b.numSamples++;
7191 23 May 23 nicklas 336         }
7185 22 May 23 nicklas 337       }
7185 22 May 23 nicklas 338       
7185 22 May 23 nicklas 339       int numEntries = specimen.size();
7185 22 May 23 nicklas 340       int entryNo = 0;
7185 22 May 23 nicklas 341       for (SampleEntry s : specimen)
7185 22 May 23 nicklas 342       {
7185 22 May 23 nicklas 343         entryNo++;
7185 22 May 23 nicklas 344         if (progress != null)
7148 08 May 23 nicklas 345         {
7185 22 May 23 nicklas 346           progress.display(5+((validateOnly?85:55)*entryNo)/numEntries, "Validating entry "+entryNo+" of " + numEntries+"...");
7148 08 May 23 nicklas 347         }
7185 22 May 23 nicklas 348         try
7142 04 May 23 nicklas 349         {
7185 22 May 23 nicklas 350           // Basic checks that all required values are present and valid
7185 22 May 23 nicklas 351           boolean validForMap = checkEntry(s);
7193 23 May 23 nicklas 352           boolean mapped = s.mappedTo != null;;
7185 22 May 23 nicklas 353           
7193 23 May 23 nicklas 354           if (validForMap && !mapped)
7143 05 May 23 nicklas 355           {
7185 22 May 23 nicklas 356             // Try to map to information in our database:
7185 22 May 23 nicklas 357             // Match rule 1: Place existing Specimen, Lysate, etc. at new box locations
7185 22 May 23 nicklas 358             if (!mapped) mapped = mapEntryToExistingSpecimenByClarityId(dc, s);
7185 22 May 23 nicklas 359     
7185 22 May 23 nicklas 360             // Match rule 2: Convert existing NoSpecimen to Specimen, create child items and place at new box locations
7185 22 May 23 nicklas 361             if (!mapped) mapped = mapEntryToExistingNoSpecimenByScanBId(dc, s);
7185 22 May 23 nicklas 362     
7185 22 May 23 nicklas 363             // Match rule 3: Create Specimen (link to existing case) and child items and place at new box locations
7185 22 May 23 nicklas 364             if (!mapped) mapped = mapEntryToExistingCaseByScanBId(dc, s);
7185 22 May 23 nicklas 365             
7185 22 May 23 nicklas 366             // Match rule 4: Create Case (link to Patient) and child items and place at new box locations
7185 22 May 23 nicklas 367             if (!mapped) mapped = mapEntryToExistingBloodByScanBId(dc, s);
7185 22 May 23 nicklas 368             
7185 22 May 23 nicklas 369             // Match rule 5: Create Case (no link to Patient) and child items and place at new box locations
7185 22 May 23 nicklas 370             if (!mapped) mapped = mapEntryToNothing(dc, s);
7185 22 May 23 nicklas 371           }
7185 22 May 23 nicklas 372           
7185 22 May 23 nicklas 373           if (mapped)
7185 22 May 23 nicklas 374           {
7185 22 May 23 nicklas 375             if (s.theCase != null || s.blood != null)
7143 05 May 23 nicklas 376             {
7185 22 May 23 nicklas 377               // Verify consent
7185 22 May 23 nicklas 378               String consent = (String)Annotationtype.CONSENT.getAnnotationValue(dc, s.theCase == null ? s.blood.getItem() : s.theCase.getItem());
7185 22 May 23 nicklas 379               if (consent != null && !consent.equals("Yes"))
7185 22 May 23 nicklas 380               {
7185 22 May 23 nicklas 381                 numNoConsent++;
7185 22 May 23 nicklas 382                 s.canImport = false;
7185 22 May 23 nicklas 383                 s.noConsent = true;
7185 22 May 23 nicklas 384                 s.action = "No import (Consent="+consent+"). Leave all tubes in the transport boxes.";
7185 22 May 23 nicklas 385               }
7143 05 May 23 nicklas 386             }
7143 05 May 23 nicklas 387           }
7185 22 May 23 nicklas 388           else if (s.errors.size() == 0)
7185 22 May 23 nicklas 389           {
7185 22 May 23 nicklas 390             // This should not really happen but we leave it here as a safe-guard
7185 22 May 23 nicklas 391             // in case we have missed an unusal case
7185 22 May 23 nicklas 392             s.addError("Could not be mapped by any existing method");
7185 22 May 23 nicklas 393           }
7143 05 May 23 nicklas 394         }
7185 22 May 23 nicklas 395         catch (RuntimeException ex)
7143 05 May 23 nicklas 396         {
7185 22 May 23 nicklas 397           s.addError(ex.getMessage());
7185 22 May 23 nicklas 398           ex.printStackTrace(System.out);
7142 04 May 23 nicklas 399         }
7142 04 May 23 nicklas 400       }
7185 22 May 23 nicklas 401   
7185 22 May 23 nicklas 402       if (progress != null) progress.display(validateOnly?95:65, "Assigning new storage box locations...");
7185 22 May 23 nicklas 403       // Assign new storage locations, but we do not auto-create new storage boxes here
7185 22 May 23 nicklas 404       SpecimenTube lastPlacedSpecimen = SpecimenTube.getLastPlacedSpecimen(dc, null, "E-Sp");
7185 22 May 23 nicklas 405       List<StoragePlate> spBoxes = StoragePlate.findExistingStorageBoxes(dc, lastPlacedSpecimen, "E-Sp");
7185 22 May 23 nicklas 406       List<BioWell> wells = StoragePlate.findEmptyWells(dc, spBoxes, specimen.size(), null); // "null" means that we do not create new boxes      
7185 22 May 23 nicklas 407       int wellNo = 0;
7185 22 May 23 nicklas 408       for (SampleEntry s : specimen)
7142 04 May 23 nicklas 409       {
7185 22 May 23 nicklas 410         if (s.canImport)
7143 05 May 23 nicklas 411         {
7185 22 May 23 nicklas 412           if (wellNo < wells.size())
7185 22 May 23 nicklas 413           {
7185 22 May 23 nicklas 414             s.well = wells.get(wellNo);
7185 22 May 23 nicklas 415             wellNo++;
7185 22 May 23 nicklas 416           }
7185 22 May 23 nicklas 417           else
7185 22 May 23 nicklas 418           {
7185 22 May 23 nicklas 419             s.addError("No storage box available");
7185 22 May 23 nicklas 420           }
7143 05 May 23 nicklas 421         }
7185 22 May 23 nicklas 422         
7185 22 May 23 nicklas 423         if (s.warnings.size() > 0) totalWarnings++;
7185 22 May 23 nicklas 424         if (validateOnly)
7185 22 May 23 nicklas 425         {
7185 22 May 23 nicklas 426           if (s.errors.size() > 0) totalErrors++;
7185 22 May 23 nicklas 427         }
7143 05 May 23 nicklas 428         else
7143 05 May 23 nicklas 429         {
7185 22 May 23 nicklas 430           // Copy errors when importing
7185 22 May 23 nicklas 431           for (String e : s.errors)
7185 22 May 23 nicklas 432           {
7185 22 May 23 nicklas 433             addErrorMessage("Line "+s.lineNo+": "+e);
7185 22 May 23 nicklas 434           }
7143 05 May 23 nicklas 435         }
7140 28 Apr 23 nicklas 436       }
7158 10 May 23 nicklas 437       
7185 22 May 23 nicklas 438       if (!validateOnly && !hasError())
7142 04 May 23 nicklas 439       {
7185 22 May 23 nicklas 440         // Create items lists -- one list per transport box and item type
7191 23 May 23 nicklas 441         JSONObject jsonBoxes = (JSONObject)requestParameters.get("boxes");
7185 22 May 23 nicklas 442         Map<String, ListSet> itemLists = new HashMap<>();
7191 23 May 23 nicklas 443         for (String boxName : boxes.keySet())
7142 04 May 23 nicklas 444         {
7191 23 May 23 nicklas 445           JSONObject jsonBox = (JSONObject)jsonBoxes.get(boxName);
7191 23 May 23 nicklas 446           itemLists.put(boxName, new ListSet(dc, boxName, importFile, jsonBox));
7185 22 May 23 nicklas 447           //addImportMessage("Created item lists for transport box: " + boxName);
7142 04 May 23 nicklas 448         }
7185 22 May 23 nicklas 449   
7185 22 May 23 nicklas 450         int sampleNo = -1;
7185 22 May 23 nicklas 451         Stats stats = new Stats();
7191 23 May 23 nicklas 452         JSONArray jsonSamples = (JSONArray)requestParameters.get("samples");
7185 22 May 23 nicklas 453         for (SampleEntry s : specimen)
7162 12 May 23 nicklas 454         {
7185 22 May 23 nicklas 455           sampleNo++;
7185 22 May 23 nicklas 456           if (progress != null)
7168 12 May 23 nicklas 457           {
7185 22 May 23 nicklas 458             progress.display(65+(30*sampleNo)/numEntries, "Creating and updating items "+sampleNo+" of "+numEntries+"...");
7168 12 May 23 nicklas 459           }
7185 22 May 23 nicklas 460           ListSet listSet = itemLists.get(s.transportBox);
7191 23 May 23 nicklas 461           JSONObject parameters = (JSONObject)jsonSamples.get(sampleNo);
7185 22 May 23 nicklas 462           s.updateFromRequestParameters(parameters);
7185 22 May 23 nicklas 463           if (!s.canImport)
7143 05 May 23 nicklas 464           {
7185 22 May 23 nicklas 465             //addImportMessage("Line "+s.lineNo+": Skipped (no consent)");
7185 22 May 23 nicklas 466             stats.skipped++;
7185 22 May 23 nicklas 467             String msg = s.clarityId+":";
7185 22 May 23 nicklas 468             if (s.action != null) msg += " "+s.action;
7185 22 May 23 nicklas 469             if (s.comment != null) msg += " "+s.comment;
7185 22 May 23 nicklas 470             if (s.action == null && s.comment == null)
7144 05 May 23 nicklas 471             {
7185 22 May 23 nicklas 472               msg += " Not imported. Leave all tubes in the transport boxes.";
7144 05 May 23 nicklas 473             }
7185 22 May 23 nicklas 474             listSet.transportMessages.add(StringUtil.trimString(msg, 255));
7143 05 May 23 nicklas 475           }
7185 22 May 23 nicklas 476           else
7143 05 May 23 nicklas 477           {
7185 22 May 23 nicklas 478             try
7185 22 May 23 nicklas 479             {
7185 22 May 23 nicklas 480               s.createMissingItems(dc, stats);
7185 22 May 23 nicklas 481               s.placeInStorageBoxes(dc);
7185 22 May 23 nicklas 482               s.addToItemLists(dc, listSet, stats);
7185 22 May 23 nicklas 483               s.appendWarnings(dc);
7185 22 May 23 nicklas 484               
7185 22 May 23 nicklas 485               //addImportMessage("Line "+s.lineNo+": Imported to "+s.specimen.getName()+(s.specimen.getId()==0?" (new)":""));
7185 22 May 23 nicklas 486               // Copy debug messages
7185 22 May 23 nicklas 487               for (String d : s.debug)
7185 22 May 23 nicklas 488               {
7185 22 May 23 nicklas 489                 addDebugMessage("Line "+s.lineNo+": "+d);
7185 22 May 23 nicklas 490               }
7185 22 May 23 nicklas 491             }
7185 22 May 23 nicklas 492             catch (RuntimeException ex)
7185 22 May 23 nicklas 493             {
7185 22 May 23 nicklas 494               addErrorMessage("Line "+s.lineNo+": "+ex.getMessage());
7185 22 May 23 nicklas 495               ex.printStackTrace(System.out);
7185 22 May 23 nicklas 496             }
7143 05 May 23 nicklas 497           }
7142 04 May 23 nicklas 498         }
7185 22 May 23 nicklas 499         
7185 22 May 23 nicklas 500         // Add TransportMessages to the item lists
7185 22 May 23 nicklas 501         for (ListSet ls : itemLists.values())
7185 22 May 23 nicklas 502         {
7185 22 May 23 nicklas 503           ls.addTransportMessages(dc);
7185 22 May 23 nicklas 504         }    
7185 22 May 23 nicklas 505         
7185 22 May 23 nicklas 506         addImportMessage("Successfully imported "+specimen.size()+" lines from "+fileName);
7185 22 May 23 nicklas 507         addImportMessage("Mapped "+stats.mappedExistingSpecimen+" lines to existing Specimen");
7185 22 May 23 nicklas 508         addImportMessage("Converted "+stats.convertedNoSpecimen+" NoSpecimen items to Specimen");
7185 22 May 23 nicklas 509         addImportMessage("Created "+stats.newSpecimen+" new Specimen items");
7193 23 May 23 nicklas 510         addImportMessage("Skipped "+stats.skipped+" lines");
7185 22 May 23 nicklas 511         addImportMessage("Added "+stats.flagged+" Specimen to the 'Flagged External Specimen' list");
7142 04 May 23 nicklas 512       }
7142 04 May 23 nicklas 513     }
7185 22 May 23 nicklas 514     finally
7185 22 May 23 nicklas 515     {
7185 22 May 23 nicklas 516       FileUtil.close(in);
7185 22 May 23 nicklas 517     }
7142 04 May 23 nicklas 518
7139 27 Apr 23 nicklas 519     return !hasError();
7139 27 Apr 23 nicklas 520   }
7139 27 Apr 23 nicklas 521   
7139 27 Apr 23 nicklas 522   public boolean checkEntry(SampleEntry s)
7139 27 Apr 23 nicklas 523   {
7141 02 May 23 nicklas 524     // Clarity ID should not be missing
7141 02 May 23 nicklas 525     if (s.clarityId == null) 
7139 27 Apr 23 nicklas 526     {
7141 02 May 23 nicklas 527       s.addError("Missing Clarity ID");
7139 27 Apr 23 nicklas 528     }
7148 08 May 23 nicklas 529     
7141 02 May 23 nicklas 530     // SCAN-B ID should not be missing
7141 02 May 23 nicklas 531     if (s.scanBId == null) 
7139 27 Apr 23 nicklas 532     {
7148 08 May 23 nicklas 533       // A missing SCANB-ID can be either a warning or error
7148 08 May 23 nicklas 534       // depending on what it maps to, so we ignore this for now
7148 08 May 23 nicklas 535       // s.addWarning("Missing SCAN-B ID");
7140 28 Apr 23 nicklas 536     }
7234 02 Jun 23 nicklas 537     else if (!s.scanBId.matches("\\d{7}(C|D|T)?"))
7140 28 Apr 23 nicklas 538     {
7141 02 May 23 nicklas 539       // and must match 7-digits + optional C or D suffix
7141 02 May 23 nicklas 540       s.addError("Invalid SCAN-B ID");
7140 28 Apr 23 nicklas 541     }
7251 08 Jun 23 nicklas 542     else if (Site.findByCaseName(s.scanBId) == Site.UNKNOWN)
7251 08 Jun 23 nicklas 543     {
7251 08 Jun 23 nicklas 544       s.addError("SCAN-B ID doesn't match a known site");
7251 08 Jun 23 nicklas 545     }
7141 02 May 23 nicklas 546     
7141 02 May 23 nicklas 547     // Transport box should not be missing
7141 02 May 23 nicklas 548     if (s.transportBox == null) 
7141 02 May 23 nicklas 549     {
7141 02 May 23 nicklas 550       s.addError("Missing transport box name");
7141 02 May 23 nicklas 551     }
7139 27 Apr 23 nicklas 552
7156 10 May 23 nicklas 553     boolean validForMap = s.errors.size() == 0;
7156 10 May 23 nicklas 554     
7193 23 May 23 nicklas 555     // A duplicated Clarity ID is an error or warning, but can never be imported
7156 10 May 23 nicklas 556     SampleEntry other = specimenByClarityId.get(s.clarityId);
7156 10 May 23 nicklas 557     if (other != null)
7156 10 May 23 nicklas 558     {
7193 23 May 23 nicklas 559       s.canImport = false;
7193 23 May 23 nicklas 560       // If the same Clarity ID references different SCAN-B ID:s we set both to an error
7193 23 May 23 nicklas 561       if (!EqualsHelper.equals(s.scanBId, other.scanBId))
7193 23 May 23 nicklas 562       {
7193 23 May 23 nicklas 563         s.addError("Duplicate Clarity ID with different SCAN-B ID (also on line "+other.lineNo+")");
7193 23 May 23 nicklas 564         s.addError(s.scanBId+"<>"+other.scanBId);
7193 23 May 23 nicklas 565         other.addError("Duplicate Clarity ID with different SCAN-B ID (also on line "+s.lineNo+")");
7193 23 May 23 nicklas 566         other.addError(s.scanBId+"<>"+other.scanBId);
7193 23 May 23 nicklas 567         other.canImport = false;
7193 23 May 23 nicklas 568         other.action = null;
7193 23 May 23 nicklas 569       }
7193 23 May 23 nicklas 570       else
7193 23 May 23 nicklas 571       {
7193 23 May 23 nicklas 572         // Otherwise it is only a warning and a "No import" action on the current sample
7193 23 May 23 nicklas 573         other.addWarning("Duplicate Clarity ID (also on line "+s.lineNo+")");
7193 23 May 23 nicklas 574         s.addWarning("Duplicate Clarity ID (also on line "+other.lineNo+")");
7193 23 May 23 nicklas 575         s.action = "No import (Duplicate). Leave extra tubes in the transport boxes.";
7193 23 May 23 nicklas 576         s.mappedTo = other.mappedTo;
7193 23 May 23 nicklas 577         validForMap = false;
7193 23 May 23 nicklas 578       }
7156 10 May 23 nicklas 579     }
7156 10 May 23 nicklas 580     specimenByClarityId.put(s.clarityId, s);
7156 10 May 23 nicklas 581     return validForMap;
7141 02 May 23 nicklas 582   }
7141 02 May 23 nicklas 583   
7141 02 May 23 nicklas 584   /**
7141 02 May 23 nicklas 585     Rule 1: Find an existing Specimen with matching Clarity ID that
7141 02 May 23 nicklas 586     has data and child items but is missing a storage location.
7141 02 May 23 nicklas 587     Returns TRUE if the mapping was successful, FALSE otherwise.
7141 02 May 23 nicklas 588   */
7141 02 May 23 nicklas 589   public boolean mapEntryToExistingSpecimenByClarityId(DbControl dc, SampleEntry s)
7141 02 May 23 nicklas 590   {
7141 02 May 23 nicklas 591     if (!s.findSpecimenByClarityId(dc)) return false;
7141 02 May 23 nicklas 592     
7143 05 May 23 nicklas 593     s.mappedTo = s.specimen.getItem();
7159 11 May 23 nicklas 594     s.comment = s.mappedTo.getDescription();
7159 11 May 23 nicklas 595     s.otherPathNote = (String)Annotationtype.OTHER_PATH_NOTE.getAnnotationValue(dc, s.mappedTo);
7143 05 May 23 nicklas 596     numMappedToSpecimen++;
7143 05 May 23 nicklas 597     
7142 04 May 23 nicklas 598     // Find all child items
7141 02 May 23 nicklas 599     s.findLysate(dc);
7141 02 May 23 nicklas 600     s.findRna(dc);
7142 04 May 23 nicklas 601     s.findDna(dc);
7142 04 May 23 nicklas 602     s.findFlowThrough(dc);
7141 02 May 23 nicklas 603       
7141 02 May 23 nicklas 604     // Check that current storage is not set 
7141 02 May 23 nicklas 605     s.checkStorage(s.specimen);
7141 02 May 23 nicklas 606     s.checkStorage(s.lysate);
7141 02 May 23 nicklas 607     s.checkStorage(s.rna);
7141 02 May 23 nicklas 608     s.checkStorage(s.dna);
7141 02 May 23 nicklas 609     s.checkStorage(s.flowThrough);
7148 08 May 23 nicklas 610     
7148 08 May 23 nicklas 611     // If both PAD and SCAN-B ID mismatch it is an error,
7148 08 May 23 nicklas 612     // otherwise a warning if a a single mismatch
7148 08 May 23 nicklas 613     String currentPad = (String)Annotationtype.PAD.getAnnotationValue(dc, s.specimen.getItem());
7155 10 May 23 nicklas 614     if (currentPad != null) currentPad = currentPad.toUpperCase(Locale.ROOT);
7148 08 May 23 nicklas 615     boolean padMismatch = s.pad != null && currentPad != null && !s.pad.contains(currentPad);
7148 08 May 23 nicklas 616     boolean scanbMismatch = s.scanBId == null || !s.specimen.getName().startsWith(s.scanBId.substring(0,  7));
7148 08 May 23 nicklas 617     if (padMismatch && scanbMismatch)
7148 08 May 23 nicklas 618     {
7158 10 May 23 nicklas 619       s.addError("Clarity ID matched a Specimen with different SCAN-B ID and PAD: "+currentPad+"<>"+s.pad);
7148 08 May 23 nicklas 620     }
7148 08 May 23 nicklas 621     else if (padMismatch)
7148 08 May 23 nicklas 622     {
7158 10 May 23 nicklas 623       s.addWarning("Clarity ID matched a Specimen with different PAD: "+currentPad+"<>"+s.pad);
7148 08 May 23 nicklas 624     }
7148 08 May 23 nicklas 625     else if (scanbMismatch)
7148 08 May 23 nicklas 626     {
7162 12 May 23 nicklas 627       s.addWarning(s.scanBId == null ? "SCAN-B ID is missing" : "Clarity ID matched a Specimen with different SCAN-B ID: "+s.specimen.getName()+"<>"+s.scanBId);
7148 08 May 23 nicklas 628     }
7158 10 May 23 nicklas 629     if (padMismatch)
7158 10 May 23 nicklas 630     {
7158 10 May 23 nicklas 631       Sample otherSampleWithPAD = s.findSampleByPad(dc, s.pad);
7158 10 May 23 nicklas 632       if (otherSampleWithPAD != null)
7158 10 May 23 nicklas 633       {
7158 10 May 23 nicklas 634         ItemSubtype otherType = otherSampleWithPAD.getItemSubtype();
7158 10 May 23 nicklas 635         s.addWarning("Found other "+(otherType==null?"sample":otherType.getName())+", "+otherSampleWithPAD.getName()+", with PAD=" + s.pad);
7158 10 May 23 nicklas 636       }
7158 10 May 23 nicklas 637       else
7158 10 May 23 nicklas 638       {
7158 10 May 23 nicklas 639         s.addWarning("Could not find any other item with PAD="+s.pad);
7158 10 May 23 nicklas 640       }
7158 10 May 23 nicklas 641     }
7148 08 May 23 nicklas 642     
7148 08 May 23 nicklas 643     // Now check for other warnings    
7142 04 May 23 nicklas 644     String exists = (String)Annotationtype.EXTERNAL_SPECIMEN_EXISTS.getAnnotationValue(dc, s.specimen.getItem());
7141 02 May 23 nicklas 645     if (!"Yes".equals(exists))
7139 27 Apr 23 nicklas 646     {
7141 02 May 23 nicklas 647       s.addWarning("Annotation ExternalSpecimenExists="+exists+" (expected 'Yes')");
7141 02 May 23 nicklas 648     }
7141 02 May 23 nicklas 649     
7141 02 May 23 nicklas 650     if (s.errors.size() == 0)
7141 02 May 23 nicklas 651     {
7141 02 May 23 nicklas 652       // The mapping is OK
7143 05 May 23 nicklas 653       s.canImport = true;
7158 10 May 23 nicklas 654       s.action = "Assign new storage locations to existing Specimen, Lysate, RNA, DNA and FT.";
7141 02 May 23 nicklas 655     }
7141 02 May 23 nicklas 656     
7148 08 May 23 nicklas 657     return s.mappedTo != null;
7141 02 May 23 nicklas 658   }
7141 02 May 23 nicklas 659   
7141 02 May 23 nicklas 660   
7141 02 May 23 nicklas 661   /**
7141 02 May 23 nicklas 662     Rule 2: Find an existing NoSpecimen with matching SCAN-B ID that
7141 02 May 23 nicklas 663     has EXTERNAL_SPECIMEN_EXISTS=Yes.
7141 02 May 23 nicklas 664   */
7141 02 May 23 nicklas 665   public boolean mapEntryToExistingNoSpecimenByScanBId(DbControl dc, SampleEntry s)
7141 02 May 23 nicklas 666   {
7141 02 May 23 nicklas 667     if (!s.findNoSpecimenByScanBId(dc)) return false;
7139 27 Apr 23 nicklas 668
7143 05 May 23 nicklas 669     s.mappedTo = s.noSpecimen.getItem();
7159 11 May 23 nicklas 670     s.comment = s.mappedTo.getDescription();
7159 11 May 23 nicklas 671     s.otherPathNote = (String)Annotationtype.OTHER_PATH_NOTE.getAnnotationValue(dc, s.mappedTo);
7143 05 May 23 nicklas 672     numMappedToNoSpecimen++;
7143 05 May 23 nicklas 673
7141 02 May 23 nicklas 674     if (s.pad != null)
7141 02 May 23 nicklas 675     {
7142 04 May 23 nicklas 676       String currentPad = (String)Annotationtype.PAD.getAnnotationValue(dc, s.noSpecimen.getItem());
7155 10 May 23 nicklas 677       if (currentPad != null) currentPad = currentPad.toUpperCase(Locale.ROOT);
7158 10 May 23 nicklas 678       if (currentPad == null || !s.pad.contains(currentPad))
7141 02 May 23 nicklas 679       {
7158 10 May 23 nicklas 680         s.addWarning("PAD mismatch: "+currentPad+"<>"+s.pad);
7158 10 May 23 nicklas 681         s.alternateAction = "Create a new Specimen, Lysate, RNA, DNA and FT and link them to this Case.";
7158 10 May 23 nicklas 682         s.alternateIsDefault = true;
7158 10 May 23 nicklas 683         Sample otherSampleWithPAD = s.findSampleByPad(dc, s.pad);
7158 10 May 23 nicklas 684         if (otherSampleWithPAD != null)
7158 10 May 23 nicklas 685         {
7158 10 May 23 nicklas 686           ItemSubtype otherType = otherSampleWithPAD.getItemSubtype();
7158 10 May 23 nicklas 687           s.addWarning("Found other "+(otherType==null?"sample":otherType.getName())+", "+otherSampleWithPAD.getName()+", with PAD=" + s.pad);
7158 10 May 23 nicklas 688         }
7158 10 May 23 nicklas 689         else
7158 10 May 23 nicklas 690         {
7158 10 May 23 nicklas 691           s.addWarning("Could not find any other item with PAD="+s.pad);
7158 10 May 23 nicklas 692         }
7141 02 May 23 nicklas 693       }
7141 02 May 23 nicklas 694     }
7141 02 May 23 nicklas 695   
7141 02 May 23 nicklas 696     if (s.errors.size() == 0)
7141 02 May 23 nicklas 697     {
7141 02 May 23 nicklas 698       // The mapping is OK
7143 05 May 23 nicklas 699       s.canImport = true;
7158 10 May 23 nicklas 700       s.action = "Convert to Specimen, and create new Lysate, RNA, DNA and FT.";
7141 02 May 23 nicklas 701     }
7141 02 May 23 nicklas 702     
7148 08 May 23 nicklas 703     return s.mappedTo != null;
7139 27 Apr 23 nicklas 704   }
7139 27 Apr 23 nicklas 705   
7139 27 Apr 23 nicklas 706   /**
7143 05 May 23 nicklas 707     Rule 3: Find an existing Case with matching SCAN-B ID.
7143 05 May 23 nicklas 708   */
7143 05 May 23 nicklas 709   public boolean mapEntryToExistingCaseByScanBId(DbControl dc, SampleEntry s)
7143 05 May 23 nicklas 710   {
7143 05 May 23 nicklas 711     if (!s.findCaseByScanBId(dc)) return false;
7143 05 May 23 nicklas 712
7143 05 May 23 nicklas 713     s.mappedTo = s.theCase.getItem();
7143 05 May 23 nicklas 714     numMappedToCase++;
7143 05 May 23 nicklas 715
7143 05 May 23 nicklas 716     if (s.errors.size() == 0)
7143 05 May 23 nicklas 717     {
7143 05 May 23 nicklas 718       // The mapping is OK
7143 05 May 23 nicklas 719       s.canImport = true;
7158 10 May 23 nicklas 720       s.action = "Create a new Specimen, Lysate, RNA, DNA and FT and link them to this Case.";
7143 05 May 23 nicklas 721     }
7143 05 May 23 nicklas 722     
7148 08 May 23 nicklas 723     return s.mappedTo != null;
7143 05 May 23 nicklas 724   }
7143 05 May 23 nicklas 725
7143 05 May 23 nicklas 726   /**
7143 05 May 23 nicklas 727     Rule 4: Find an existing Blood with matching SCAN-B ID.
7143 05 May 23 nicklas 728   */
7143 05 May 23 nicklas 729   public boolean mapEntryToExistingBloodByScanBId(DbControl dc, SampleEntry s)
7143 05 May 23 nicklas 730   {
7143 05 May 23 nicklas 731     if (!s.findBloodByScanBId(dc)) return false;
7143 05 May 23 nicklas 732   
7143 05 May 23 nicklas 733     s.mappedTo = s.blood.getItem();
7143 05 May 23 nicklas 734     numMappedToBlood++;
7143 05 May 23 nicklas 735   
7143 05 May 23 nicklas 736     if (s.errors.size() == 0)
7143 05 May 23 nicklas 737     {
7143 05 May 23 nicklas 738       // The mapping is OK
7143 05 May 23 nicklas 739       s.canImport = true;
7158 10 May 23 nicklas 740       s.action = "Create a new Case, Specimen, Lysate, RNA, DNA and FT and link them to the Patient.";
7143 05 May 23 nicklas 741     }
7143 05 May 23 nicklas 742     
7148 08 May 23 nicklas 743     return s.mappedTo != null;
7143 05 May 23 nicklas 744   }
7143 05 May 23 nicklas 745
7143 05 May 23 nicklas 746   /**
7143 05 May 23 nicklas 747     Rule 5: Map the entry to nothing.
7143 05 May 23 nicklas 748   */
7143 05 May 23 nicklas 749   public boolean mapEntryToNothing(DbControl dc, SampleEntry s)
7143 05 May 23 nicklas 750   {
7143 05 May 23 nicklas 751     numMappedToNothing++;
7157 10 May 23 nicklas 752     
7157 10 May 23 nicklas 753     if (s.scanBId == null)
7157 10 May 23 nicklas 754     {
7177 17 May 23 nicklas 755       s.canImport = false;
7177 17 May 23 nicklas 756       s.addWarning("SCAN-B ID is missing");
7177 17 May 23 nicklas 757       s.action = "No import (Not a SCAN-B sample). Leave all tubes in the transport boxes.";
7157 10 May 23 nicklas 758     }
7177 17 May 23 nicklas 759     else
7143 05 May 23 nicklas 760     {
7177 17 May 23 nicklas 761       if (s.errors.size() == 0)
7177 17 May 23 nicklas 762       {
7177 17 May 23 nicklas 763         // The mapping is OK
7177 17 May 23 nicklas 764         s.canImport = true;
7177 17 May 23 nicklas 765         s.action = "Create a new Specimen, Lysate, RNA, DNA and FT (not linked to any Patient).";
7177 17 May 23 nicklas 766       }
7143 05 May 23 nicklas 767     }
7143 05 May 23 nicklas 768     
7157 10 May 23 nicklas 769     return s.errors.size() == 0;
7143 05 May 23 nicklas 770   }
7143 05 May 23 nicklas 771   
7143 05 May 23 nicklas 772   /**
7139 27 Apr 23 nicklas 773     Get a mapper for a required column. If the column doesn't exists
7139 27 Apr 23 nicklas 774     an error message is logged and null is returned.
7139 27 Apr 23 nicklas 775   */
7139 27 Apr 23 nicklas 776   private Mapper getRequiredMapper(FlatFileParser ffp, String col, String fileAndLine)
7139 27 Apr 23 nicklas 777   {
7139 27 Apr 23 nicklas 778     Mapper mapper = null;
7139 27 Apr 23 nicklas 779     if (ffp.getColumnHeaderIndex(col) == null)
7139 27 Apr 23 nicklas 780     {
7139 27 Apr 23 nicklas 781       addErrorMessage(fileAndLine+"Column '" + col + "' not found in column headers.");
7139 27 Apr 23 nicklas 782     }
7139 27 Apr 23 nicklas 783     else
7139 27 Apr 23 nicklas 784     {
7139 27 Apr 23 nicklas 785       mapper = ffp.getMapper("\\" + col + "\\");
7139 27 Apr 23 nicklas 786     }
7139 27 Apr 23 nicklas 787     return mapper;
7139 27 Apr 23 nicklas 788   }
7139 27 Apr 23 nicklas 789   
7139 27 Apr 23 nicklas 790   /**
7139 27 Apr 23 nicklas 791     Get a mapper for an optional column. If the column doesn't exists
7139 27 Apr 23 nicklas 792     a warning message is logged (unless 'fileAndLine' is null) 
7139 27 Apr 23 nicklas 793     and mapper that always return null is returned.
7139 27 Apr 23 nicklas 794   */
7139 27 Apr 23 nicklas 795   private Mapper getOptionalMapper(FlatFileParser ffp, String col, String fileAndLine)
7139 27 Apr 23 nicklas 796   {
7139 27 Apr 23 nicklas 797     Mapper mapper = null;
7139 27 Apr 23 nicklas 798     if (ffp.getColumnHeaderIndex(col) == null)
7139 27 Apr 23 nicklas 799     {
7139 27 Apr 23 nicklas 800       if (fileAndLine != null)
7139 27 Apr 23 nicklas 801       {
7139 27 Apr 23 nicklas 802         addWarningMessage(fileAndLine+"Column '" + col + "' not found in column headers.");
7139 27 Apr 23 nicklas 803       }
7139 27 Apr 23 nicklas 804       mapper = new ConstantMapper((String)null);
7139 27 Apr 23 nicklas 805     }
7139 27 Apr 23 nicklas 806     else
7139 27 Apr 23 nicklas 807     {
7139 27 Apr 23 nicklas 808       mapper = ffp.getMapper("\\" + col + "\\");
7139 27 Apr 23 nicklas 809     }
7139 27 Apr 23 nicklas 810     return mapper;
7139 27 Apr 23 nicklas 811   }
7139 27 Apr 23 nicklas 812
7191 23 May 23 nicklas 813   public static class Box
7191 23 May 23 nicklas 814   {
7191 23 May 23 nicklas 815     final String name;
7191 23 May 23 nicklas 816     int numSamples;
7191 23 May 23 nicklas 817     
7191 23 May 23 nicklas 818     public Box(String name)
7191 23 May 23 nicklas 819     {
7191 23 May 23 nicklas 820       this.name = name;
7191 23 May 23 nicklas 821     }
7191 23 May 23 nicklas 822     
7191 23 May 23 nicklas 823     public JSONObject toJSONObject()
7191 23 May 23 nicklas 824     {
7191 23 May 23 nicklas 825       JSONObject json = new JSONObject();
7191 23 May 23 nicklas 826       json.put("name", name);
7191 23 May 23 nicklas 827       json.put("numSamples", numSamples);
7191 23 May 23 nicklas 828       return json;
7191 23 May 23 nicklas 829     }
7191 23 May 23 nicklas 830   }
7191 23 May 23 nicklas 831   
7139 27 Apr 23 nicklas 832   public static class SampleEntry
7139 27 Apr 23 nicklas 833   {
7176 17 May 23 nicklas 834     final String line;
7139 27 Apr 23 nicklas 835     final String fileAndLine;
7139 27 Apr 23 nicklas 836     final int lineNo;
7139 27 Apr 23 nicklas 837     String clarityId;
7139 27 Apr 23 nicklas 838     String scanBId;
7139 27 Apr 23 nicklas 839     String pad;
7140 28 Apr 23 nicklas 840     String transportBox;
7141 02 May 23 nicklas 841     
7142 04 May 23 nicklas 842     Patient patient;
7142 04 May 23 nicklas 843     Case theCase;
7143 05 May 23 nicklas 844     Blood blood;
7140 28 Apr 23 nicklas 845     BioWell well;
7142 04 May 23 nicklas 846     SpecimenTube specimen;
7142 04 May 23 nicklas 847     NoSpecimen noSpecimen;
7142 04 May 23 nicklas 848     Lysate lysate;
7142 04 May 23 nicklas 849     Rna rna;
7142 04 May 23 nicklas 850     Dna dna;
7142 04 May 23 nicklas 851     FlowThrough flowThrough;
7139 27 Apr 23 nicklas 852     
7143 05 May 23 nicklas 853     boolean canImport;
7148 08 May 23 nicklas 854     boolean noConsent;
7164 12 May 23 nicklas 855     boolean flag;
7143 05 May 23 nicklas 856     MeasuredBioMaterial mappedTo;
7159 11 May 23 nicklas 857     String comment;
7159 11 May 23 nicklas 858     String otherPathNote;
7143 05 May 23 nicklas 859     String action;
7158 10 May 23 nicklas 860     String alternateAction;
7158 10 May 23 nicklas 861     boolean alternateIsDefault;
7141 02 May 23 nicklas 862     final List<String> errors;
7141 02 May 23 nicklas 863     final List<String> warnings;
7144 05 May 23 nicklas 864     final List<String> debug;
7139 27 Apr 23 nicklas 865     
7176 17 May 23 nicklas 866     public SampleEntry(String line, String fileAndLine, int lineNo)
7139 27 Apr 23 nicklas 867     {
7176 17 May 23 nicklas 868       this.line = line;
7139 27 Apr 23 nicklas 869       this.fileAndLine = fileAndLine;
7139 27 Apr 23 nicklas 870       this.lineNo = lineNo;
7141 02 May 23 nicklas 871       this.errors = new ArrayList<>();
7141 02 May 23 nicklas 872       this.warnings = new ArrayList<>();
7144 05 May 23 nicklas 873       this.debug = new ArrayList<>();
7139 27 Apr 23 nicklas 874     }
7139 27 Apr 23 nicklas 875     
7141 02 May 23 nicklas 876     public void addError(String error)
7141 02 May 23 nicklas 877     {
7141 02 May 23 nicklas 878       this.errors.add(error);
7141 02 May 23 nicklas 879     }
7141 02 May 23 nicklas 880     
7141 02 May 23 nicklas 881     public void addWarning(String warning)
7141 02 May 23 nicklas 882     {
7141 02 May 23 nicklas 883       this.warnings.add(warning);
7141 02 May 23 nicklas 884     }
7141 02 May 23 nicklas 885     
7144 05 May 23 nicklas 886     public void addDebug(String msg)
7144 05 May 23 nicklas 887     {
7144 05 May 23 nicklas 888       this.debug.add(msg);
7144 05 May 23 nicklas 889     }
7144 05 May 23 nicklas 890     
7139 27 Apr 23 nicklas 891     public JSONObject toJSONObject()
7139 27 Apr 23 nicklas 892     {
7139 27 Apr 23 nicklas 893       JSONObject json = new JSONObject();
7176 17 May 23 nicklas 894       json.put("line", line);
7139 27 Apr 23 nicklas 895       json.put("lineNo", lineNo);
7139 27 Apr 23 nicklas 896       json.put("clarityId", clarityId);
7139 27 Apr 23 nicklas 897       json.put("scanBId", scanBId);
7139 27 Apr 23 nicklas 898       json.put("pad", pad);
7140 28 Apr 23 nicklas 899       json.put("transportBox", transportBox);
7140 28 Apr 23 nicklas 900       if (well != null) json.put("well", JsonUtil.getBioWellAsJSON(well, true));
7140 28 Apr 23 nicklas 901       
7148 08 May 23 nicklas 902       json.put("noConsent", noConsent);
7143 05 May 23 nicklas 903       json.put("canImport", canImport);
7143 05 May 23 nicklas 904       json.put("action", action);
7158 10 May 23 nicklas 905       json.put("alternateAction", alternateAction);
7158 10 May 23 nicklas 906       json.put("alternateIsDefault", alternateIsDefault);
7159 11 May 23 nicklas 907       json.put("comment", comment);
7159 11 May 23 nicklas 908       json.put("otherPathNote", otherPathNote);
7143 05 May 23 nicklas 909       if (mappedTo != null)
7143 05 May 23 nicklas 910       {
7143 05 May 23 nicklas 911         JSONObject jsonMapped = new JSONObject();
7143 05 May 23 nicklas 912         jsonMapped.put("name", mappedTo.getName());
7143 05 May 23 nicklas 913         jsonMapped.put("id", mappedTo.getId());
7143 05 May 23 nicklas 914         jsonMapped.put("itemType", mappedTo.getItemSubtype().getName());
7143 05 May 23 nicklas 915         json.put("mappedTo", jsonMapped);
7143 05 May 23 nicklas 916       }
7143 05 May 23 nicklas 917       
7141 02 May 23 nicklas 918       if (errors.size() > 0)
7141 02 May 23 nicklas 919       {
7141 02 May 23 nicklas 920         JSONArray jsonErrors = new JSONArray();
7141 02 May 23 nicklas 921         jsonErrors.addAll(errors);
7141 02 May 23 nicklas 922         json.put("errors", jsonErrors);
7141 02 May 23 nicklas 923       }
7141 02 May 23 nicklas 924       if (warnings.size() > 0)
7141 02 May 23 nicklas 925       {
7141 02 May 23 nicklas 926         JSONArray jsonWarnings = new JSONArray();
7141 02 May 23 nicklas 927         jsonWarnings.addAll(warnings);
7141 02 May 23 nicklas 928         json.put("warnings", jsonWarnings);
7141 02 May 23 nicklas 929       }
7139 27 Apr 23 nicklas 930       return json;
7139 27 Apr 23 nicklas 931     }
7141 02 May 23 nicklas 932     
7161 11 May 23 nicklas 933     void updateFromRequestParameters(JSONObject json)
7161 11 May 23 nicklas 934     {
7161 11 May 23 nicklas 935       if (json == null) return;
7161 11 May 23 nicklas 936       if (!clarityId.equals(json.get("clarityId")))
7161 11 May 23 nicklas 937       {
7161 11 May 23 nicklas 938         throw new InvalidDataException("Clarity ID mismatch: "+clarityId+"<>"+json.get("clarityId"));
7161 11 May 23 nicklas 939       }
7161 11 May 23 nicklas 940       
7161 11 May 23 nicklas 941       String comment = Values.getStringOrNull((String)json.get("comment"));
7161 11 May 23 nicklas 942       if (comment != null) this.comment = comment;
7161 11 May 23 nicklas 943       
7161 11 May 23 nicklas 944       if (alternateAction != null && !Boolean.TRUE.equals(json.get("alternateAction")))
7161 11 May 23 nicklas 945       {
7161 11 May 23 nicklas 946         // Reset the alternate action
7161 11 May 23 nicklas 947         alternateAction = null;
7161 11 May 23 nicklas 948       }
7161 11 May 23 nicklas 949     }
7161 11 May 23 nicklas 950     
7141 02 May 23 nicklas 951     /**
7141 02 May 23 nicklas 952       Try to find an existing Specimen item with EXTERNA_REF=cmd:Clarity ID
7141 02 May 23 nicklas 953       Return TRUE if a single match was found, FALSE otherwise.
7141 02 May 23 nicklas 954     */
7141 02 May 23 nicklas 955     boolean findSpecimenByClarityId(DbControl dc)
7141 02 May 23 nicklas 956     {
7141 02 May 23 nicklas 957       if (clarityId == null) return false;
7141 02 May 23 nicklas 958       
7141 02 May 23 nicklas 959       ItemQuery<Sample> findSpecimen = Sample.getQuery();
7141 02 May 23 nicklas 960       Subtype.SPECIMEN.addFilter(dc, findSpecimen);
7141 02 May 23 nicklas 961       findSpecimen.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
7141 02 May 23 nicklas 962       findSpecimen.join(Annotations.innerJoin(Annotationtype.EXTERNAL_REF.get(dc), "cid"));
7141 02 May 23 nicklas 963       findSpecimen.restrict(Restrictions.eq(Hql.alias("cid"), Expressions.string("cmd:"+clarityId)));
7141 02 May 23 nicklas 964       
7141 02 May 23 nicklas 965       List<Sample> samples = findSpecimen.list(dc);
7141 02 May 23 nicklas 966       if (samples.size() > 1)
7141 02 May 23 nicklas 967       {
7141 02 May 23 nicklas 968         // This should *never* happen
7141 02 May 23 nicklas 969         addError("Found " + samples.size() + " Specimen with Clarity ID: "+clarityId);
7141 02 May 23 nicklas 970       }
7141 02 May 23 nicklas 971       // In most cases we will find a matching Specimen, but
7141 02 May 23 nicklas 972       // no match is not an error condition (until we have checked other possibilities)
7141 02 May 23 nicklas 973       if (samples.size() == 1)
7141 02 May 23 nicklas 974       {
7142 04 May 23 nicklas 975         specimen = SpecimenTube.get(samples.get(0));
7143 05 May 23 nicklas 976         theCase = specimen.getCase();
7141 02 May 23 nicklas 977       }
7141 02 May 23 nicklas 978       return specimen != null;
7141 02 May 23 nicklas 979     }
7141 02 May 23 nicklas 980     
7158 10 May 23 nicklas 981     
7158 10 May 23 nicklas 982     Sample findSampleByPad(DbControl dc, String otherPad)
7158 10 May 23 nicklas 983     {
7158 10 May 23 nicklas 984       int sep = otherPad.indexOf('-');
7158 10 May 23 nicklas 985       if (sep > 0) otherPad = otherPad.substring(0, sep);
7158 10 May 23 nicklas 986       
7158 10 May 23 nicklas 987       ItemQuery<Sample> query = Sample.getQuery();
7158 10 May 23 nicklas 988       query.restrict(Restrictions.or(
7158 10 May 23 nicklas 989         Subtype.SPECIMEN.restriction(dc, null),
7158 10 May 23 nicklas 990         Subtype.NO_SPECIMEN.restriction(dc, null)
7158 10 May 23 nicklas 991       ));
7158 10 May 23 nicklas 992       query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
7158 10 May 23 nicklas 993       query.restrict(new AnnotationSimpleRestriction(null, Annotationtype.PAD.get(dc), Operator.EQ, otherPad, new AnnotationRestriction.Options()));
7158 10 May 23 nicklas 994       List<Sample> other = query.list(dc);
7158 10 May 23 nicklas 995
7158 10 May 23 nicklas 996       return other.size() > 0 ? other.get(0) : null;
7158 10 May 23 nicklas 997     }
7158 10 May 23 nicklas 998     
7142 04 May 23 nicklas 999     /**
7142 04 May 23 nicklas 1000       Try to find an existing NoSpecimen item with EXTERNAL_SPECIMEN_EXISTS=Yes
7142 04 May 23 nicklas 1001     */
7141 02 May 23 nicklas 1002     boolean findNoSpecimenByScanBId(DbControl dc)
7141 02 May 23 nicklas 1003     {
7141 02 May 23 nicklas 1004       if (scanBId == null) return false;
7141 02 May 23 nicklas 1005       
7141 02 May 23 nicklas 1006       List<NoSpecimen> nsList = NoSpecimen.findByCaseName(dc, scanBId, true);
7141 02 May 23 nicklas 1007       for (NoSpecimen ns : nsList)
7141 02 May 23 nicklas 1008       {
7141 02 May 23 nicklas 1009         if ("Yes".equals(Annotationtype.EXTERNAL_SPECIMEN_EXISTS.getAnnotationValue(dc, ns.getItem())))
7141 02 May 23 nicklas 1010         {
7142 04 May 23 nicklas 1011           noSpecimen  = ns;
7141 02 May 23 nicklas 1012           break;
7141 02 May 23 nicklas 1013         }
7141 02 May 23 nicklas 1014       }
7143 05 May 23 nicklas 1015       if (noSpecimen != null)
7143 05 May 23 nicklas 1016       {
7143 05 May 23 nicklas 1017         theCase = noSpecimen.getCase();
7143 05 May 23 nicklas 1018       }
7141 02 May 23 nicklas 1019       return noSpecimen != null;
7141 02 May 23 nicklas 1020     }
7141 02 May 23 nicklas 1021     
7141 02 May 23 nicklas 1022     /**
7143 05 May 23 nicklas 1023       Try to find an existing Case either directly or via another
7143 05 May 23 nicklas 1024       Specimen/NoSpecimen item.
7143 05 May 23 nicklas 1025     */
7143 05 May 23 nicklas 1026     boolean findCaseByScanBId(DbControl dc)
7143 05 May 23 nicklas 1027     {
7143 05 May 23 nicklas 1028       if (scanBId == null) return false;
7143 05 May 23 nicklas 1029       theCase = Case.findByName(dc, scanBId);
7143 05 May 23 nicklas 1030       return theCase != null;
7143 05 May 23 nicklas 1031     }
7143 05 May 23 nicklas 1032     
7143 05 May 23 nicklas 1033     /**
7143 05 May 23 nicklas 1034       Try to find an existing Blood item. If more than
7143 05 May 23 nicklas 1035       one is found we simply take the first one since we are
7143 05 May 23 nicklas 1036       mainly interested in the patient.
7143 05 May 23 nicklas 1037     */
7143 05 May 23 nicklas 1038     boolean findBloodByScanBId(DbControl dc)
7143 05 May 23 nicklas 1039     {
7143 05 May 23 nicklas 1040       if (scanBId == null) return false;
7143 05 May 23 nicklas 1041       List<Blood> list = Blood.findAllByCaseName(dc, scanBId);
7143 05 May 23 nicklas 1042       if (list.size() > 0) blood = list.get(0);
7144 05 May 23 nicklas 1043       if (blood != null)
7144 05 May 23 nicklas 1044       {
7144 05 May 23 nicklas 1045         patient = Patient.findByBlood(dc, blood);
7144 05 May 23 nicklas 1046       }
7143 05 May 23 nicklas 1047       return blood != null;
7143 05 May 23 nicklas 1048     }
7143 05 May 23 nicklas 1049   
7143 05 May 23 nicklas 1050     /**
7141 02 May 23 nicklas 1051       Find a matching Lysate item that is a child item to the current Specimen.
7141 02 May 23 nicklas 1052       It must find exactly one item otherwise it is an error.
7141 02 May 23 nicklas 1053     */
7141 02 May 23 nicklas 1054     boolean findLysate(DbControl dc)
7141 02 May 23 nicklas 1055     {
7142 04 May 23 nicklas 1056       lysate = Lysate.get(findChild(dc, specimen, Subtype.LYSATE));
7142 04 May 23 nicklas 1057       return lysate != null;
7141 02 May 23 nicklas 1058     }
7141 02 May 23 nicklas 1059     
7141 02 May 23 nicklas 1060     /**
7141 02 May 23 nicklas 1061       Find a matching RNA item that is a child item to the current Lysate.
7141 02 May 23 nicklas 1062       It must find exactly one item otherwise it is an error.
7141 02 May 23 nicklas 1063     */
7141 02 May 23 nicklas 1064     boolean findRna(DbControl dc)
7141 02 May 23 nicklas 1065     {
7142 04 May 23 nicklas 1066       rna = Rna.get(findChild(dc, lysate, Subtype.RNA));
7142 04 May 23 nicklas 1067       return rna != null;
7142 04 May 23 nicklas 1068     }
7142 04 May 23 nicklas 1069   
7142 04 May 23 nicklas 1070     /**
7142 04 May 23 nicklas 1071       Find a matching DNA item that is a child item to the current Lysate.
7142 04 May 23 nicklas 1072       It must find exactly one item otherwise it is an error.
7142 04 May 23 nicklas 1073     */
7142 04 May 23 nicklas 1074     boolean findDna(DbControl dc)
7142 04 May 23 nicklas 1075     {
7142 04 May 23 nicklas 1076       dna = Dna.get(findChild(dc, lysate, Subtype.DNA));
7142 04 May 23 nicklas 1077       return dna != null;
7142 04 May 23 nicklas 1078     }
7142 04 May 23 nicklas 1079
7142 04 May 23 nicklas 1080     /**
7142 04 May 23 nicklas 1081       Find a matching FlowThrough item that is a child item to the current Lysate.
7142 04 May 23 nicklas 1082       It must find exactly one item otherwise it is an error.
7142 04 May 23 nicklas 1083     */
7142 04 May 23 nicklas 1084     boolean findFlowThrough(DbControl dc)
7142 04 May 23 nicklas 1085     {
7142 04 May 23 nicklas 1086       flowThrough = FlowThrough.get(findChild(dc, lysate, Subtype.FLOW_THROUGH));
7142 04 May 23 nicklas 1087       return flowThrough != null;
7142 04 May 23 nicklas 1088     }
7142 04 May 23 nicklas 1089     
7142 04 May 23 nicklas 1090     private Extract findChild(DbControl dc, ReggieItem<? extends MeasuredBioMaterial> parent, Subtype childType)
7142 04 May 23 nicklas 1091     {
7142 04 May 23 nicklas 1092       if (parent == null) return null;
7141 02 May 23 nicklas 1093       
7142 04 May 23 nicklas 1094       ItemQuery<Extract> find = Extract.getQuery();
7142 04 May 23 nicklas 1095       childType.addFilter(dc, find);
7142 04 May 23 nicklas 1096       find.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
7142 04 May 23 nicklas 1097       find.join(Hql.innerJoin("parent", "p"));
7142 04 May 23 nicklas 1098       find.restrict(Restrictions.eq(Hql.alias("p"), Hql.entity(parent.getItem())));
7141 02 May 23 nicklas 1099       
7142 04 May 23 nicklas 1100       List<Extract> items = find.list(dc);
7141 02 May 23 nicklas 1101       if (items.size() == 0)
7141 02 May 23 nicklas 1102       {
7142 04 May 23 nicklas 1103         addError("Could not find child "+childType.getName()+" for: "+lysate.getName());
7142 04 May 23 nicklas 1104         return null;
7141 02 May 23 nicklas 1105       }
7141 02 May 23 nicklas 1106       else if (items.size() > 1)
7141 02 May 23 nicklas 1107       {
7142 04 May 23 nicklas 1108         addError("Found " + items.size() + " "+childType.getName()+" for: "+lysate.getName());
7142 04 May 23 nicklas 1109         return null;
7141 02 May 23 nicklas 1110       }
7142 04 May 23 nicklas 1111       return items.get(0);
7141 02 May 23 nicklas 1112     }
7141 02 May 23 nicklas 1113
7142 04 May 23 nicklas 1114     /**
7142 04 May 23 nicklas 1115       Check that the given item is not already stored in some place.
7142 04 May 23 nicklas 1116     */
7142 04 May 23 nicklas 1117     void checkStorage(ReggieItem<? extends MeasuredBioMaterial> item)
7141 02 May 23 nicklas 1118     {
7142 04 May 23 nicklas 1119       if (item != null && item.getItem().isInWell())
7141 02 May 23 nicklas 1120       {
7142 04 May 23 nicklas 1121         addError(item.getItem().getItemSubtype().getName()+
7142 04 May 23 nicklas 1122           " is already stored: "+getStorageLocation(item.getItem().getBioWell()));
7141 02 May 23 nicklas 1123       }
7141 02 May 23 nicklas 1124     }
7141 02 May 23 nicklas 1125     
7142 04 May 23 nicklas 1126     /**
7142 04 May 23 nicklas 1127       Get the name of the storage location (eg. name of box+coordinates)
7142 04 May 23 nicklas 1128     */
7141 02 May 23 nicklas 1129     String getStorageLocation(BioWell well)
7141 02 May 23 nicklas 1130     {
7141 02 May 23 nicklas 1131       if (well == null) return null;
7141 02 May 23 nicklas 1132       BioPlate plate = well.getPlate();
7141 02 May 23 nicklas 1133       return plate.getName()+" "+well.getCoordinate();
7141 02 May 23 nicklas 1134     }
7141 02 May 23 nicklas 1135
7142 04 May 23 nicklas 1136     /**
7142 04 May 23 nicklas 1137       If we could not map to existing items, they need to be created and
7142 04 May 23 nicklas 1138       properly linked to whatever information we have. We must ensure that
7142 04 May 23 nicklas 1139       a Specimen, Lysate, RNA, DNA and FlowThrough always exists. If
7142 04 May 23 nicklas 1140       a new Specimen is needed we will also try to link to an existing
7142 04 May 23 nicklas 1141       Patient or Case. 
7142 04 May 23 nicklas 1142     */
7162 12 May 23 nicklas 1143     void createMissingItems(DbControl dc, Stats stats)
7142 04 May 23 nicklas 1144     {
7142 04 May 23 nicklas 1145       
7144 05 May 23 nicklas 1146       if (theCase == null)
7144 05 May 23 nicklas 1147       {
7144 05 May 23 nicklas 1148         // We may need to create a Case but only if it can be linked to a patient
7144 05 May 23 nicklas 1149         if (patient != null)
7144 05 May 23 nicklas 1150         {
7144 05 May 23 nicklas 1151           Sample c = Sample.getNew(dc);
7144 05 May 23 nicklas 1152           c.setItemSubtype(Subtype.CASE.get(dc));
7144 05 May 23 nicklas 1153           c.setExternalId(Case.getNextExternalId(dc));
7144 05 May 23 nicklas 1154           c.setName(scanBId.substring(0,  7)); // It may hava suffix
7144 05 May 23 nicklas 1155           BioMaterialEvent ce = c.getCreationEvent();
7144 05 May 23 nicklas 1156           ce.setSource(patient.getItem());
7144 05 May 23 nicklas 1157           ce.setProtocol(null);
7144 05 May 23 nicklas 1158           ce.setHardware(null);
7144 05 May 23 nicklas 1159           if (blood != null) // It shouldn't be since the case can only be null if we found the patient via blood
7144 05 May 23 nicklas 1160           {
7144 05 May 23 nicklas 1161             // Copy consent
7144 05 May 23 nicklas 1162             Consent.copyConsentAnnotations(dc, blood.getItem(), c, false);
7144 05 May 23 nicklas 1163           }
7144 05 May 23 nicklas 1164           dc.saveItem(c);
7145 05 May 23 nicklas 1165           theCase = Case.get(c);
7144 05 May 23 nicklas 1166           addDebug("Created new Case: "+c.getName());
7144 05 May 23 nicklas 1167         }
7144 05 May 23 nicklas 1168       }
7145 05 May 23 nicklas 1169       else
7145 05 May 23 nicklas 1170       {
7145 05 May 23 nicklas 1171         addDebug("Using existing Case: "+theCase.getName());
7145 05 May 23 nicklas 1172       }
7144 05 May 23 nicklas 1173       
7142 04 May 23 nicklas 1174       if (specimen == null)
7142 04 May 23 nicklas 1175       {
7142 04 May 23 nicklas 1176         Sample sp = null;
7161 11 May 23 nicklas 1177         if (noSpecimen != null && alternateAction == null)
7142 04 May 23 nicklas 1178         {
7142 04 May 23 nicklas 1179           sp = noSpecimen.getItem();
7142 04 May 23 nicklas 1180           noSpecimen = null;
7144 05 May 23 nicklas 1181           addDebug("Converting NoSpecimen to Specimen: "+sp.getName());
7162 12 May 23 nicklas 1182           stats.convertedNoSpecimen++;
7142 04 May 23 nicklas 1183         }
7142 04 May 23 nicklas 1184         else
7142 04 May 23 nicklas 1185         {
7144 05 May 23 nicklas 1186           sp = Sample.getNew(dc);
7144 05 May 23 nicklas 1187           sp.setExternalId(SpecimenTube.getNextExternalId(dc, Subtype.SPECIMEN));
7144 05 May 23 nicklas 1188           sp.setName(SpecimenTube.getNextSpecimenName(dc, scanBId, true));
7144 05 May 23 nicklas 1189           BioMaterialEvent ce = sp.getCreationEvent();
7144 05 May 23 nicklas 1190           if (theCase != null)
7142 04 May 23 nicklas 1191           {
7144 05 May 23 nicklas 1192             ce.setSource(theCase.getItem());
7145 05 May 23 nicklas 1193             Annotationtype.LATERALITY.copyAnnotationValues(dc, theCase.getItem(), sp, false);
7142 04 May 23 nicklas 1194           }
7144 05 May 23 nicklas 1195           ce.setProtocol(null);
7144 05 May 23 nicklas 1196           ce.setHardware(null);
7142 04 May 23 nicklas 1197           dc.saveItem(sp);
7145 05 May 23 nicklas 1198           if (scanBId.endsWith("C")) Annotationtype.BIOPSY_TYPE.setAnnotationValue(dc, sp, "SpecimenCoreBiopsy");
7145 05 May 23 nicklas 1199           if (scanBId.endsWith("D")) Annotationtype.BIOPSY_TYPE.setAnnotationValue(dc, sp, "SpecimenCoreBiopsy2nd");
7144 05 May 23 nicklas 1200           addDebug("Created new Specimen: "+sp.getName());
7162 12 May 23 nicklas 1201           stats.newSpecimen++;
7142 04 May 23 nicklas 1202         }
7142 04 May 23 nicklas 1203         
7142 04 May 23 nicklas 1204         sp.setItemSubtype(Subtype.SPECIMEN.get(dc));
7142 04 May 23 nicklas 1205         Annotationtype.TUBE_LABEL.setAnnotationValue(dc, sp, clarityId);
7142 04 May 23 nicklas 1206         Annotationtype.EXTERNAL_REF.setAnnotationValue(dc, sp, "cmd:"+clarityId);
7163 12 May 23 nicklas 1207         Annotationtype.AUTO_PROCESSING.setAnnotationValue(dc, sp, "Disable");
7144 05 May 23 nicklas 1208         if (pad != null && !Annotationtype.PAD.hasAnnotation(dc, sp))
7144 05 May 23 nicklas 1209         {
7144 05 May 23 nicklas 1210           Annotationtype.PAD.setAnnotationValue(dc, sp, pad);
7144 05 May 23 nicklas 1211         }
7142 04 May 23 nicklas 1212         specimen = SpecimenTube.get(sp);
7164 12 May 23 nicklas 1213         flag = true;
7142 04 May 23 nicklas 1214       }
7144 05 May 23 nicklas 1215       else
7144 05 May 23 nicklas 1216       {
7144 05 May 23 nicklas 1217         addDebug("Using existing Specimen: "+specimen.getName());
7162 12 May 23 nicklas 1218         stats.mappedExistingSpecimen++;
7144 05 May 23 nicklas 1219       }
7142 04 May 23 nicklas 1220       
7161 11 May 23 nicklas 1221       // Update comment
7161 11 May 23 nicklas 1222       if (comment != null) specimen.getItem().setDescription(comment);
7161 11 May 23 nicklas 1223       
7142 04 May 23 nicklas 1224       if (lysate == null)
7142 04 May 23 nicklas 1225       {
7142 04 May 23 nicklas 1226         Extract lys = createChildExtract(dc, specimen, Subtype.LYSATE);
7142 04 May 23 nicklas 1227         Annotationtype.TUBE_LABEL.setAnnotationValue(dc, lys, clarityId+"-Lys");
7142 04 May 23 nicklas 1228         lysate = Lysate.get(lys);
7144 05 May 23 nicklas 1229         addDebug("Created new Lysate: "+lys.getName());
7142 04 May 23 nicklas 1230       }
7142 04 May 23 nicklas 1231       
7142 04 May 23 nicklas 1232       if (rna == null)
7142 04 May 23 nicklas 1233       {
7142 04 May 23 nicklas 1234         Extract r = createChildExtract(dc, lysate, Subtype.RNA);
7142 04 May 23 nicklas 1235         Annotationtype.TUBE_LABEL.setAnnotationValue(dc, r, clarityId+"-RNA");
7142 04 May 23 nicklas 1236         rna = Rna.get(r);
7144 05 May 23 nicklas 1237         addDebug("Created new RNA: "+r.getName());
7142 04 May 23 nicklas 1238       }
7142 04 May 23 nicklas 1239       
7142 04 May 23 nicklas 1240       if (dna == null)
7142 04 May 23 nicklas 1241       {
7142 04 May 23 nicklas 1242         Extract d = createChildExtract(dc, lysate, Subtype.DNA);
7142 04 May 23 nicklas 1243         Annotationtype.TUBE_LABEL.setAnnotationValue(dc, d, clarityId+"-DNA");
7142 04 May 23 nicklas 1244         dna = Dna.get(d);
7144 05 May 23 nicklas 1245         addDebug("Created new DNA: "+d.getName());
7142 04 May 23 nicklas 1246       }
7142 04 May 23 nicklas 1247       
7142 04 May 23 nicklas 1248       if (flowThrough == null)
7142 04 May 23 nicklas 1249       {
7142 04 May 23 nicklas 1250         Extract ft = createChildExtract(dc, lysate, Subtype.FLOW_THROUGH);
7142 04 May 23 nicklas 1251         Annotationtype.TUBE_LABEL.setAnnotationValue(dc, ft, clarityId+"-Prot");
7142 04 May 23 nicklas 1252         flowThrough = FlowThrough.get(ft);
7144 05 May 23 nicklas 1253         addDebug("Created new FlowThough: "+ft.getName());
7142 04 May 23 nicklas 1254       }
7142 04 May 23 nicklas 1255     }
7142 04 May 23 nicklas 1256     
7142 04 May 23 nicklas 1257     private Extract createChildExtract(DbControl dc, ReggieItem<? extends MeasuredBioMaterial> parent, Subtype subtype)
7142 04 May 23 nicklas 1258     {
7142 04 May 23 nicklas 1259       Extract e = Extract.getNew(dc);
7142 04 May 23 nicklas 1260       e.setItemSubtype(subtype.get(dc));
7142 04 May 23 nicklas 1261       e.setName(ReggieItem.getNextChildItemName(dc, parent.getName(), null, subtype.getItemSuffix(), true));
7142 04 May 23 nicklas 1262       BioMaterialEvent createEvent = e.getCreationEvent();
7142 04 May 23 nicklas 1263       createEvent.setSource(parent.getItem());
7142 04 May 23 nicklas 1264       createEvent.setProtocol(null);
7142 04 May 23 nicklas 1265       createEvent.setHardware(null);
7163 12 May 23 nicklas 1266       Annotationtype.AUTO_PROCESSING.setAnnotationValue(dc, e, "Disable");
7142 04 May 23 nicklas 1267       dc.saveItem(e);
7142 04 May 23 nicklas 1268       return e;
7142 04 May 23 nicklas 1269     }
7142 04 May 23 nicklas 1270     
7142 04 May 23 nicklas 1271     /**
7142 04 May 23 nicklas 1272       Place existing items in storage boxes.
7142 04 May 23 nicklas 1273     */
7142 04 May 23 nicklas 1274     void placeInStorageBoxes(DbControl dc)
7142 04 May 23 nicklas 1275     {
7142 04 May 23 nicklas 1276       specimen.getItem().setBioWell(well);
7142 04 May 23 nicklas 1277       BioWell lysWell = StoragePlate.getChildBioWell(dc, well, "E-Sp", "E-Lys");
7142 04 May 23 nicklas 1278       lysate.getItem().setBioWell(lysWell);
7142 04 May 23 nicklas 1279       rna.getItem().setBioWell(StoragePlate.getChildBioWell(dc, lysWell, "E-Lys", "E-RNA"));
7142 04 May 23 nicklas 1280       dna.getItem().setBioWell(StoragePlate.getChildBioWell(dc, lysWell, "E-Lys", "E-DNA"));
7142 04 May 23 nicklas 1281       flowThrough.getItem().setBioWell(StoragePlate.getChildBioWell(dc, lysWell, "E-Lys", "E-FT"));
7142 04 May 23 nicklas 1282       
7145 05 May 23 nicklas 1283       // Set/Update EXTERNAL_STORAGE annotation and EXTERNAL_SPECIMEN_EXISTS
7145 05 May 23 nicklas 1284       Annotationtype.EXTERNAL_SPECIMEN_EXISTS.setAnnotationValue(dc, specimen.getItem(), "Yes");
7142 04 May 23 nicklas 1285       String extStorage = "Transport box: " + transportBox;
7142 04 May 23 nicklas 1286       Annotationtype.EXTERNAL_STORAGE.setAnnotationValue(dc, specimen.getItem(), extStorage);
7142 04 May 23 nicklas 1287       Annotationtype.EXTERNAL_STORAGE.setAnnotationValue(dc, lysate.getItem(), extStorage);
7142 04 May 23 nicklas 1288       Annotationtype.EXTERNAL_STORAGE.setAnnotationValue(dc, rna.getItem(), extStorage);
7142 04 May 23 nicklas 1289       Annotationtype.EXTERNAL_STORAGE.setAnnotationValue(dc, dna.getItem(), extStorage);
7142 04 May 23 nicklas 1290       Annotationtype.EXTERNAL_STORAGE.setAnnotationValue(dc, flowThrough.getItem(), extStorage);
7142 04 May 23 nicklas 1291     }
7142 04 May 23 nicklas 1292     
7142 04 May 23 nicklas 1293     /**
7142 04 May 23 nicklas 1294       Add items to the item lists that are used for the lab protocol.
7142 04 May 23 nicklas 1295     */
7164 12 May 23 nicklas 1296     void addToItemLists(DbControl dc, ListSet ls, Stats stats)
7142 04 May 23 nicklas 1297     {
7142 04 May 23 nicklas 1298       ls.specimenList.add(specimen.getItem());
7142 04 May 23 nicklas 1299       ls.lysateList.add(lysate.getItem());
7142 04 May 23 nicklas 1300       ls.rnaList.add(rna.getItem());
7142 04 May 23 nicklas 1301       ls.dnaList.add(dna.getItem());
7142 04 May 23 nicklas 1302       ls.flowThroughList.add(flowThrough.getItem());
7164 12 May 23 nicklas 1303       if (flag) 
7164 12 May 23 nicklas 1304       {
7164 12 May 23 nicklas 1305         if (ls.flaggedExternalSpecimen.add(specimen.getItem()))
7164 12 May 23 nicklas 1306         {
7164 12 May 23 nicklas 1307           stats.flagged++;
7164 12 May 23 nicklas 1308         }
7164 12 May 23 nicklas 1309         
7164 12 May 23 nicklas 1310       }
7142 04 May 23 nicklas 1311     }
7166 12 May 23 nicklas 1312     
7166 12 May 23 nicklas 1313     /**
7166 12 May 23 nicklas 1314       Add all warnings to the ImportWarnings annotation.
7166 12 May 23 nicklas 1315     */
7166 12 May 23 nicklas 1316     @SuppressWarnings("unchecked")
7166 12 May 23 nicklas 1317     void appendWarnings(DbControl dc)
7166 12 May 23 nicklas 1318     {
7166 12 May 23 nicklas 1319       if (warnings.size() > 0)
7166 12 May 23 nicklas 1320       {
7166 12 May 23 nicklas 1321         List<String> existingWarnings = (List<String>)Annotationtype.IMPORT_WARNINGS.getAnnotationValues(dc, specimen.getItem());
7166 12 May 23 nicklas 1322         List<String> all = new ArrayList<>(existingWarnings);
7166 12 May 23 nicklas 1323         all.addAll(warnings);
7166 12 May 23 nicklas 1324         Annotationtype.IMPORT_WARNINGS.setAnnotationValues(dc, mappedTo, all);
7166 12 May 23 nicklas 1325       }
7166 12 May 23 nicklas 1326     }
7139 27 Apr 23 nicklas 1327   }
7139 27 Apr 23 nicklas 1328
7142 04 May 23 nicklas 1329   /**
7142 04 May 23 nicklas 1330     Hold the 5 different ItemList items that are
7142 04 May 23 nicklas 1331     related to a single transport box.
7142 04 May 23 nicklas 1332   */
7142 04 May 23 nicklas 1333   public static class ListSet
7142 04 May 23 nicklas 1334   {
7142 04 May 23 nicklas 1335     final String boxName;
7185 22 May 23 nicklas 1336     final File importedFrom;
7191 23 May 23 nicklas 1337     final JSONObject jsonInfo;
7142 04 May 23 nicklas 1338     
7168 12 May 23 nicklas 1339     final List<String> transportMessages;
7164 12 May 23 nicklas 1340     final ItemList flaggedExternalSpecimen;
7142 04 May 23 nicklas 1341     final ItemList specimenList;
7142 04 May 23 nicklas 1342     final ItemList lysateList;
7142 04 May 23 nicklas 1343     final ItemList rnaList;
7142 04 May 23 nicklas 1344     final ItemList dnaList;
7142 04 May 23 nicklas 1345     final ItemList flowThroughList;
7142 04 May 23 nicklas 1346
7191 23 May 23 nicklas 1347     ListSet(DbControl dc, String boxName, File importedFrom, JSONObject jsonInfo)
7142 04 May 23 nicklas 1348     {
7142 04 May 23 nicklas 1349       this.boxName = boxName;
7185 22 May 23 nicklas 1350       this.importedFrom = importedFrom;
7191 23 May 23 nicklas 1351       this.jsonInfo = jsonInfo;
7168 12 May 23 nicklas 1352       this.transportMessages = new ArrayList<>();
7164 12 May 23 nicklas 1353       flaggedExternalSpecimen = BiomaterialList.FLAGGED_EXERNAL_SPECIMEN.get(dc);
7142 04 May 23 nicklas 1354       specimenList = create(dc, Subtype.SPECIMEN);
7142 04 May 23 nicklas 1355       lysateList = create(dc, Subtype.LYSATE);
7142 04 May 23 nicklas 1356       rnaList = create(dc, Subtype.RNA);
7142 04 May 23 nicklas 1357       dnaList = create(dc, Subtype.DNA);
7142 04 May 23 nicklas 1358       flowThroughList = create(dc, Subtype.FLOW_THROUGH);
7142 04 May 23 nicklas 1359     }
7142 04 May 23 nicklas 1360     
7142 04 May 23 nicklas 1361     private ItemList create(DbControl dc, Subtype subtype)
7142 04 May 23 nicklas 1362     {
7142 04 May 23 nicklas 1363       ItemList list = ItemList.getNew(dc, subtype.getMainType());
7142 04 May 23 nicklas 1364       list.setItemSubtype(subtype.get(dc));
7142 04 May 23 nicklas 1365       list.setExternalId(WORK_LIST_ID);
7142 04 May 23 nicklas 1366       list.setName("Transport box: "+boxName + " ("+subtype.getName()+")");
7191 23 May 23 nicklas 1367       list.setDescription((String)jsonInfo.get("comment"));
7174 17 May 23 nicklas 1368       Annotationtype.EXTERNAL_REF.setAnnotationValue(dc, list, boxName);
7142 04 May 23 nicklas 1369       dc.saveItem(list);
7185 22 May 23 nicklas 1370       
7185 22 May 23 nicklas 1371       AnyToAny link = AnyToAny.getNewOrExisting(dc, list, "Imported from", importedFrom, true);
7185 22 May 23 nicklas 1372       dc.saveItem(link);
7185 22 May 23 nicklas 1373
7142 04 May 23 nicklas 1374       return list;
7142 04 May 23 nicklas 1375     }
7168 12 May 23 nicklas 1376     
7168 12 May 23 nicklas 1377     public void addTransportMessages(DbControl dc)
7168 12 May 23 nicklas 1378     {
7168 12 May 23 nicklas 1379       if (transportMessages.size() > 0)
7168 12 May 23 nicklas 1380       {
7168 12 May 23 nicklas 1381         Annotationtype.TRANSPORT_MESSAGES.setAnnotationValues(dc, specimenList, transportMessages);
7168 12 May 23 nicklas 1382         Annotationtype.TRANSPORT_MESSAGES.setAnnotationValues(dc, lysateList, transportMessages);
7168 12 May 23 nicklas 1383         Annotationtype.TRANSPORT_MESSAGES.setAnnotationValues(dc, rnaList, transportMessages);
7168 12 May 23 nicklas 1384         Annotationtype.TRANSPORT_MESSAGES.setAnnotationValues(dc, dnaList, transportMessages);
7168 12 May 23 nicklas 1385         Annotationtype.TRANSPORT_MESSAGES.setAnnotationValues(dc, flowThroughList, transportMessages);
7168 12 May 23 nicklas 1386       }
7168 12 May 23 nicklas 1387     }
7142 04 May 23 nicklas 1388   }
7142 04 May 23 nicklas 1389   
7162 12 May 23 nicklas 1390   
7162 12 May 23 nicklas 1391   public static class Stats
7162 12 May 23 nicklas 1392   {
7162 12 May 23 nicklas 1393     int mappedExistingSpecimen = 0;
7162 12 May 23 nicklas 1394     int convertedNoSpecimen = 0;
7162 12 May 23 nicklas 1395     int newSpecimen = 0;
7162 12 May 23 nicklas 1396     int skipped = 0;
7162 12 May 23 nicklas 1397     int placeInBoxes = 0;
7164 12 May 23 nicklas 1398     int flagged = 0;
7162 12 May 23 nicklas 1399   }
7139 27 Apr 23 nicklas 1400 }