extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/plugins/release/SpecimenWriter.java

Code
Comments
Other
Rev Date Author Line
3939 16 May 16 nicklas 1 package net.sf.basedb.reggie.plugins.release;
3939 16 May 16 nicklas 2
5117 20 Nov 18 nicklas 3 import java.io.IOException;
5117 20 Nov 18 nicklas 4 import java.io.InputStream;
4437 31 Mar 17 nicklas 5 import java.time.LocalDateTime;
4437 31 Mar 17 nicklas 6 import java.time.temporal.ChronoUnit;
4480 05 May 17 nicklas 7 import java.util.ArrayList;
4469 26 Apr 17 nicklas 8 import java.util.List;
3939 16 May 16 nicklas 9
4437 31 Mar 17 nicklas 10 import org.json.simple.JSONArray;
4371 02 Mar 17 nicklas 11 import org.json.simple.JSONObject;
4371 02 Mar 17 nicklas 12
5117 20 Nov 18 nicklas 13 import net.sf.basedb.core.AnyToAny;
3939 16 May 16 nicklas 14 import net.sf.basedb.core.BioMaterialEvent;
3939 16 May 16 nicklas 15 import net.sf.basedb.core.DbControl;
5117 20 Nov 18 nicklas 16 import net.sf.basedb.core.File;
4437 31 Mar 17 nicklas 17 import net.sf.basedb.core.Item;
5117 20 Nov 18 nicklas 18 import net.sf.basedb.core.ItemNotFoundException;
4462 21 Apr 17 nicklas 19 import net.sf.basedb.core.Quantity;
3939 16 May 16 nicklas 20 import net.sf.basedb.core.Sample;
4437 31 Mar 17 nicklas 21 import net.sf.basedb.core.Type;
4484 09 May 17 nicklas 22 import net.sf.basedb.core.Unit;
5117 20 Nov 18 nicklas 23 import net.sf.basedb.core.plugin.ExportOutputStream;
4480 05 May 17 nicklas 24 import net.sf.basedb.reggie.Site;
4437 31 Mar 17 nicklas 25 import net.sf.basedb.reggie.converter.MultiplyFloatConverter;
3939 16 May 16 nicklas 26 import net.sf.basedb.reggie.dao.Annotationtype;
4464 24 Apr 17 nicklas 27 import net.sf.basedb.reggie.dao.Histology;
5097 15 Nov 18 nicklas 28 import net.sf.basedb.reggie.dao.SpecimenTube;
4437 31 Mar 17 nicklas 29 import net.sf.basedb.reggie.json.FilteredJSONArray;
5117 20 Nov 18 nicklas 30 import net.sf.basedb.reggie.json.FilteredJSONObject;
5117 20 Nov 18 nicklas 31 import net.sf.basedb.util.FileUtil;
4437 31 Mar 17 nicklas 32 import net.sf.basedb.util.filter.NotNullFilter;
4462 21 Apr 17 nicklas 33 import net.sf.basedb.util.units.UnitUtil;
3939 16 May 16 nicklas 34
3939 16 May 16 nicklas 35 /**
3939 16 May 16 nicklas 36   Cohort writer implementation for the Sample data table.
3939 16 May 16 nicklas 37   @since 4.5
3939 16 May 16 nicklas 38 */
3939 16 May 16 nicklas 39 public class SpecimenWriter 
3939 16 May 16 nicklas 40   extends CohortWriter 
3939 16 May 16 nicklas 41 {
3939 16 May 16 nicklas 42
4437 31 Mar 17 nicklas 43   private final MultiplyFloatConverter toMilliG;
5117 20 Nov 18 nicklas 44   private final OutputLocation location;
5117 20 Nov 18 nicklas 45   private final ScriptWriter script;
5117 20 Nov 18 nicklas 46   
5117 20 Nov 18 nicklas 47   public SpecimenWriter(DbControl dc, ReleaseWriterOptions options, OutputLocation location, ScriptWriter script)
3939 16 May 16 nicklas 48   {
5090 14 Nov 18 nicklas 49     super(dc, options);
4437 31 Mar 17 nicklas 50     this.toMilliG = new MultiplyFloatConverter(0.001f); // Convert µg to mg
5117 20 Nov 18 nicklas 51     this.location = location;
5117 20 Nov 18 nicklas 52     this.script = script;
3939 16 May 16 nicklas 53   }
3939 16 May 16 nicklas 54   
3944 17 May 16 nicklas 55   
4371 02 Mar 17 nicklas 56   @Override
5091 14 Nov 18 nicklas 57   public JSONArray toJSONObjects(CohortItem item)
4371 02 Mar 17 nicklas 58   {
5097 15 Nov 18 nicklas 59     List<SpecimenTube> specimens = item.getSpecimens();
5097 15 Nov 18 nicklas 60     if (specimens.size() == 0) return null;
5117 20 Nov 18 nicklas 61     DbControl dc = item.getDbControl();
5140 22 Nov 18 nicklas 62     ReleaseWriterOptions options = getOptions();
4371 02 Mar 17 nicklas 63     
5097 15 Nov 18 nicklas 64     JSONArray json = new JSONArray();
5097 15 Nov 18 nicklas 65     for (SpecimenTube sp : specimens)
4480 05 May 17 nicklas 66     {
5097 15 Nov 18 nicklas 67       Sample specimen = sp.getItem();
5097 15 Nov 18 nicklas 68       BioMaterialEvent creationEvent = specimen.getCreationEvent();
5097 15 Nov 18 nicklas 69       Sample theCase = sp.getCase().getItem();
5107 19 Nov 18 nicklas 70       
5097 15 Nov 18 nicklas 71       Histology histology = item.getHistology(sp);
5231 15 Jan 19 nicklas 72       Sample goodStain = histology == null ? null : item.getGoodStainSample(histology);
5107 19 Nov 18 nicklas 73       
5097 15 Nov 18 nicklas 74       JSONObject jsonSp = new JSONObject();
5097 15 Nov 18 nicklas 75       jsonSp.put("name", specimen.getExternalId());
5097 15 Nov 18 nicklas 76       jsonSp.put("type", specimen.getType().name());
5097 15 Nov 18 nicklas 77       jsonSp.put("subtype", getName(specimen.getItemSubtype()));
5097 15 Nov 18 nicklas 78       jsonSp.put("parent", theCase.getExternalId());
5097 15 Nov 18 nicklas 79       
5097 15 Nov 18 nicklas 80       LocalDateTime arrivalDate = item.getDateTimeAnnotation(Annotationtype.ARRIVAL_DATE, specimen);
5097 15 Nov 18 nicklas 81       LocalDateTime samplingDateTime = item.getDateTimeAnnotation(Annotationtype.SAMPLING_DATETIME, specimen);
5097 15 Nov 18 nicklas 82       LocalDateTime rnaLaterDateTime = item.getDateTimeAnnotation(Annotationtype.RNALATER_DATETIME, specimen);
5097 15 Nov 18 nicklas 83       LocalDateTime refDate = item.getDateTimeAnnotation(Annotationtype.REFERENCE_DATE, theCase);
5097 15 Nov 18 nicklas 84       
5097 15 Nov 18 nicklas 85       Site site = Site.findByCaseName(specimen.getName());
5097 15 Nov 18 nicklas 86       
5097 15 Nov 18 nicklas 87       JSONArray jsonAnnotations = new FilteredJSONArray(new NotNullFilter<>(false));
5097 15 Nov 18 nicklas 88       if (site != null && site != Site.UNKNOWN)
4437 31 Mar 17 nicklas 89       {
5097 15 Nov 18 nicklas 90         jsonAnnotations.add(item.createAnnotationJSON("Site", site.getName()));
4437 31 Mar 17 nicklas 91       }
5097 15 Nov 18 nicklas 92       jsonAnnotations.add(item.createAnnotationJSON("OriginalQuantity", 
5097 15 Nov 18 nicklas 93         toMilliG.convert(specimen.getOriginalQuantity())));
5165 03 Dec 18 nicklas 94       jsonAnnotations.add(item.createAnnotationJSON("RemainingQuantity", 
5165 03 Dec 18 nicklas 95         toMilliG.convert(specimen.getRemainingQuantity())));
5097 15 Nov 18 nicklas 96       
5097 15 Nov 18 nicklas 97       if (samplingDateTime != null && rnaLaterDateTime != null)
5097 15 Nov 18 nicklas 98       {
5097 15 Nov 18 nicklas 99         // Note that a time of 00:00 indicates that only the date is known and we can't export this value
5097 15 Nov 18 nicklas 100         if (samplingDateTime.isBefore(rnaLaterDateTime) && !atMidnight(samplingDateTime) && !atMidnight(rnaLaterDateTime))
5097 15 Nov 18 nicklas 101         {
5097 15 Nov 18 nicklas 102           Integer minToRNALater = (int)(ChronoUnit.MINUTES.between(samplingDateTime, rnaLaterDateTime));
5097 15 Nov 18 nicklas 103           jsonAnnotations.add(item.createAnnotationJSON("MinutesToRNALater", minToRNALater));
5097 15 Nov 18 nicklas 104         }
5097 15 Nov 18 nicklas 105       }
5097 15 Nov 18 nicklas 106       if (samplingDateTime != null && arrivalDate != null)
5097 15 Nov 18 nicklas 107       {
5097 15 Nov 18 nicklas 108         // Truncate to midnight before comparing or it will miss one day
5097 15 Nov 18 nicklas 109         Integer daysToLab = (int)ChronoUnit.DAYS.between(samplingDateTime.truncatedTo(ChronoUnit.DAYS), arrivalDate);
5097 15 Nov 18 nicklas 110         jsonAnnotations.add(item.createAnnotationJSON("DaysToLab", daysToLab));
5097 15 Nov 18 nicklas 111       }
5097 15 Nov 18 nicklas 112       if (samplingDateTime != null && refDate != null)
5097 15 Nov 18 nicklas 113       {
5097 15 Nov 18 nicklas 114         Integer samplingDays = (int)ChronoUnit.DAYS.between(refDate, samplingDateTime.truncatedTo(ChronoUnit.DAYS));
5097 15 Nov 18 nicklas 115         jsonAnnotations.add(item.createAnnotationJSON("SamplingDate", samplingDays));
5097 15 Nov 18 nicklas 116       }
5097 15 Nov 18 nicklas 117       
5097 15 Nov 18 nicklas 118       jsonAnnotations.add(item.getAnnotationJSON(Annotationtype.BIOPSY_TYPE, specimen, null));
5097 15 Nov 18 nicklas 119       jsonAnnotations.add(item.getAnnotationJSON(Annotationtype.SPECIMEN_TYPE, specimen, null));
5097 15 Nov 18 nicklas 120       jsonAnnotations.add(item.getAnnotationJSON(Annotationtype.LATERALITY, specimen, null));
5097 15 Nov 18 nicklas 121       jsonAnnotations.add(item.getAnnotationJSON(Annotationtype.NOF_PIECES, specimen, null));
5097 15 Nov 18 nicklas 122       jsonAnnotations.add(item.getAnnotationJSON(Annotationtype.LINKED_SPECIMEN, specimen, null));
5107 19 Nov 18 nicklas 123         
5097 15 Nov 18 nicklas 124       if (histology != null)
5097 15 Nov 18 nicklas 125       {
5117 20 Nov 18 nicklas 126         String hisName = item.toReleaseId(histology.getName());
5117 20 Nov 18 nicklas 127         jsonAnnotations.add(item.createAnnotationJSON("HisName", hisName));
5117 20 Nov 18 nicklas 128         
5117 20 Nov 18 nicklas 129         if (goodStain != null)
5117 20 Nov 18 nicklas 130         {
5117 20 Nov 18 nicklas 131           jsonAnnotations.add(item.createAnnotationJSON("HisScoreComplete", item.getAnnotationValue(Annotationtype.SCORE_COMPLETE, goodStain)));
5117 20 Nov 18 nicklas 132           jsonAnnotations.add(item.createAnnotationJSON("HisScoreInvasiveCancer", item.getAnnotationValue(Annotationtype.SCORE_INVASIVE_CANCER, goodStain)));
5117 20 Nov 18 nicklas 133           jsonAnnotations.add(item.createAnnotationJSON("HisScoreInsituCancer", item.getAnnotationValue(Annotationtype.SCORE_INSITU_CANCER, goodStain)));
5117 20 Nov 18 nicklas 134           jsonAnnotations.add(item.createAnnotationJSON("HisScoreLymphocytes", item.getAnnotationValue(Annotationtype.SCORE_LYMPHOCYTES, goodStain)));
5117 20 Nov 18 nicklas 135           jsonAnnotations.add(item.createAnnotationJSON("HisScoreStroma", item.getAnnotationValue(Annotationtype.SCORE_STROMA, goodStain)));
5117 20 Nov 18 nicklas 136           jsonAnnotations.add(item.createAnnotationJSON("HisScoreFat", item.getAnnotationValue(Annotationtype.SCORE_FAT, goodStain)));
5117 20 Nov 18 nicklas 137           jsonAnnotations.add(item.createAnnotationJSON("HisScoreNormal", item.getAnnotationValue(Annotationtype.SCORE_NORMAL, goodStain)));
5117 20 Nov 18 nicklas 138           
5140 22 Nov 18 nicklas 139           if (!options.skipHistologyImages())
5117 20 Nov 18 nicklas 140           {
5140 22 Nov 18 nicklas 141             String releaseDataFilesFolder = "/" + specimen.getExternalId();
5140 22 Nov 18 nicklas 142             JSONArray jsonFiles = exportHistologyImage(dc, goodStain, releaseDataFilesFolder, hisName + ".jpg");
5140 22 Nov 18 nicklas 143             if (jsonFiles.size() > 0) 
5117 20 Nov 18 nicklas 144             {
5140 22 Nov 18 nicklas 145               jsonAnnotations.add(item.createAnnotationJSON("DataFilesFolder", releaseDataFilesFolder));
5140 22 Nov 18 nicklas 146               jsonSp.put("files", jsonFiles);
5117 20 Nov 18 nicklas 147             }
5117 20 Nov 18 nicklas 148           }
5117 20 Nov 18 nicklas 149         }
5097 15 Nov 18 nicklas 150       }
5097 15 Nov 18 nicklas 151       
5097 15 Nov 18 nicklas 152       jsonSp.put("annotations", jsonAnnotations);
5097 15 Nov 18 nicklas 153       json.add(jsonSp);
4437 31 Mar 17 nicklas 154     }
4437 31 Mar 17 nicklas 155     
5097 15 Nov 18 nicklas 156     return json;
4371 02 Mar 17 nicklas 157   }
4437 31 Mar 17 nicklas 158   
5140 22 Nov 18 nicklas 159   /**
5140 22 Nov 18 nicklas 160     Exports the Histology image to the release archive. If the same image already exists
5140 22 Nov 18 nicklas 161     in an earlier release a symbolic link is added to the ScriptWriter, otherwise the
5140 22 Nov 18 nicklas 162     image is copied to the final position. If the goodStain sample doesn't have any
5140 22 Nov 18 nicklas 163     image nothing is done and an empty array is returned.
5140 22 Nov 18 nicklas 164   */
5140 22 Nov 18 nicklas 165   private JSONArray exportHistologyImage(DbControl dc, Sample goodStain, String releaseDataFilesFolder, String imageName)
5140 22 Nov 18 nicklas 166   {
5140 22 Nov 18 nicklas 167     // Handle the image of the histology slide
5140 22 Nov 18 nicklas 168     // On the Relax server they should be located in the top-level folder for the specimen
5140 22 Nov 18 nicklas 169     // and have a name based on the HisName annotation
5140 22 Nov 18 nicklas 170     String imagePath = releaseDataFilesFolder + "/" + imageName;
5140 22 Nov 18 nicklas 171     
5140 22 Nov 18 nicklas 172     // Check if the image already exists in the release archive
5140 22 Nov 18 nicklas 173     String releasedInVersion = location.findReleasedFile(imagePath);
5140 22 Nov 18 nicklas 174     
5140 22 Nov 18 nicklas 175     JSONObject jsonImage = new FilteredJSONObject(new NotNullFilter<>(false));
5140 22 Nov 18 nicklas 176     jsonImage.put("link", imageName);
5140 22 Nov 18 nicklas 177     jsonImage.put("name", imageName);
5140 22 Nov 18 nicklas 178     JSONArray jsonFiles = new JSONArray();
5140 22 Nov 18 nicklas 179     if (releasedInVersion != null)
5140 22 Nov 18 nicklas 180     {
5140 22 Nov 18 nicklas 181       // We can safely let the script writer handle this since it should create a symbolic link
5140 22 Nov 18 nicklas 182       jsonFiles.add(jsonImage);
5140 22 Nov 18 nicklas 183       script.addLinkToExistingFile(releaseDataFilesFolder, imageName, releasedInVersion);
5140 22 Nov 18 nicklas 184     }
5140 22 Nov 18 nicklas 185     else
5140 22 Nov 18 nicklas 186     {
5140 22 Nov 18 nicklas 187       // We need to copy the images from the BASE file system to the release archive
5140 22 Nov 18 nicklas 188       // BUT!!! we also get here if there is no image at all in which case we will get
5140 22 Nov 18 nicklas 189       // exception and doesn't export any files at all
5140 22 Nov 18 nicklas 190       InputStream imageIn = null;
5140 22 Nov 18 nicklas 191       ExportOutputStream imageOut = null;
5140 22 Nov 18 nicklas 192       try
5140 22 Nov 18 nicklas 193       {
5140 22 Nov 18 nicklas 194         // The image should be linked from the 'goodStain' sample via the name 'image'
5140 22 Nov 18 nicklas 195         AnyToAny imageLink = AnyToAny.getByName(dc, goodStain, "image");
5140 22 Nov 18 nicklas 196         File image = (File)imageLink.getTo();
5140 22 Nov 18 nicklas 197         imageIn = image.getDownloadStream(0);
5140 22 Nov 18 nicklas 198         imageOut = location.getOutputStream(imagePath, false);
5140 22 Nov 18 nicklas 199         imageOut.setMimeType(image.getMimeType());
5140 22 Nov 18 nicklas 200         FileUtil.copy(imageIn, imageOut);
5140 22 Nov 18 nicklas 201         // We should copy the metadata as well
5140 22 Nov 18 nicklas 202         jsonImage.put("size", image.getSize());
5140 22 Nov 18 nicklas 203         jsonImage.put("mimeType", image.getMimeType());
5140 22 Nov 18 nicklas 204         jsonImage.put("characterSet", image.getCharacterSet());
5140 22 Nov 18 nicklas 205         jsonFiles.add(jsonImage);
5140 22 Nov 18 nicklas 206       }
5140 22 Nov 18 nicklas 207       catch (ItemNotFoundException | IOException ex) 
5140 22 Nov 18 nicklas 208       {} // Ignore since not all histology items have images yet
5140 22 Nov 18 nicklas 209       finally
5140 22 Nov 18 nicklas 210       {
5140 22 Nov 18 nicklas 211         FileUtil.close(imageIn);
5140 22 Nov 18 nicklas 212         FileUtil.close(imageOut);
5140 22 Nov 18 nicklas 213       }
5140 22 Nov 18 nicklas 214     }
5140 22 Nov 18 nicklas 215     return jsonFiles;
5140 22 Nov 18 nicklas 216   }
5140 22 Nov 18 nicklas 217   
4437 31 Mar 17 nicklas 218   @Override
4469 26 Apr 17 nicklas 219   public List<CohortTypeDef> getTypeDefsInJSON()
4437 31 Mar 17 nicklas 220   {
4437 31 Mar 17 nicklas 221     DbControl dc = getDbControl();
3939 16 May 16 nicklas 222
4469 26 Apr 17 nicklas 223     CohortTypeDefFactory spFactory = new CohortTypeDefFactory(dc, Item.SAMPLE, "Specimen");
4464 24 Apr 17 nicklas 224     
4480 05 May 17 nicklas 225     List<String> allSites = new ArrayList<String>();
4480 05 May 17 nicklas 226     for (Site s : Site.getAllSites())
4480 05 May 17 nicklas 227     {
4480 05 May 17 nicklas 228       allSites.add(s.getName());
4480 05 May 17 nicklas 229     }
4480 05 May 17 nicklas 230     
4484 09 May 17 nicklas 231     Unit days = UnitUtil.getUnit(dc, Quantity.TIME, "d");
5165 03 Dec 18 nicklas 232     Unit mg = UnitUtil.getUnit(dc, Quantity.MASS, "mg");
4484 09 May 17 nicklas 233     
4480 05 May 17 nicklas 234     spFactory.createAnnotationType("Site", Type.STRING).setEnumeration(allSites);
5165 03 Dec 18 nicklas 235     spFactory.createAnnotationType("OriginalQuantity", Type.FLOAT).setUnit(mg);
5165 03 Dec 18 nicklas 236     spFactory.createAnnotationType("RemainingQuantity", Type.FLOAT).setUnit(mg).setProjectSpecificValues(true);
4437 31 Mar 17 nicklas 237     
4437 31 Mar 17 nicklas 238     // Calculated from ArrivalDate - SamplingDate 
4484 09 May 17 nicklas 239     spFactory.createAnnotationType("DaysToLab", Type.INT).setUnit(days);
4462 21 Apr 17 nicklas 240
4437 31 Mar 17 nicklas 241     // Calculated from RNALaterDateTime - SamplingDateTime
4469 26 Apr 17 nicklas 242     spFactory.createAnnotationType("MinutesToRNALater", Type.INT).setUnit(UnitUtil.getUnit(dc, Quantity.TIME, "min"));
4484 09 May 17 nicklas 243     
4484 09 May 17 nicklas 244     // Calculated from SamplingDate - ReferenceDate (on the case)
4484 09 May 17 nicklas 245     spFactory.createAnnotationType("SamplingDate", Type.INT).setUnit(days).setProjectSpecificValues(true);
4468 26 Apr 17 nicklas 246
4469 26 Apr 17 nicklas 247     spFactory.createAnnotationType(Annotationtype.BIOPSY_TYPE);
4469 26 Apr 17 nicklas 248     spFactory.createAnnotationType(Annotationtype.SPECIMEN_TYPE);
5973 24 Jun 20 nicklas 249     spFactory.createAnnotationType(Annotationtype.LATERALITY).setProjectSpecificValues(true);
4469 26 Apr 17 nicklas 250     spFactory.createAnnotationType(Annotationtype.NOF_PIECES);
4469 26 Apr 17 nicklas 251     spFactory.createAnnotationType(Annotationtype.LINKED_SPECIMEN).setProjectSpecificValues(true);
4468 26 Apr 17 nicklas 252
5117 20 Nov 18 nicklas 253     // Need folder for Histology images
5117 20 Nov 18 nicklas 254     spFactory.createAnnotationType(Annotationtype.DATA_FILES_FOLDER).setProjectSpecificValues(true);
5117 20 Nov 18 nicklas 255     
4468 26 Apr 17 nicklas 256     spFactory.setNamePrefix("His");
5107 19 Nov 18 nicklas 257     spFactory.setProjectSpecificValues(true);
4469 26 Apr 17 nicklas 258     spFactory.createAnnotationType("Name", Type.STRING);
4469 26 Apr 17 nicklas 259     spFactory.createAnnotationType(Annotationtype.SCORE_COMPLETE);
4469 26 Apr 17 nicklas 260     spFactory.createAnnotationType(Annotationtype.SCORE_INVASIVE_CANCER);
4469 26 Apr 17 nicklas 261     spFactory.createAnnotationType(Annotationtype.SCORE_INSITU_CANCER);
4469 26 Apr 17 nicklas 262     spFactory.createAnnotationType(Annotationtype.SCORE_LYMPHOCYTES);
4469 26 Apr 17 nicklas 263     spFactory.createAnnotationType(Annotationtype.SCORE_STROMA);
4469 26 Apr 17 nicklas 264     spFactory.createAnnotationType(Annotationtype.SCORE_FAT);
4469 26 Apr 17 nicklas 265     spFactory.createAnnotationType(Annotationtype.SCORE_NORMAL);
4468 26 Apr 17 nicklas 266
4468 26 Apr 17 nicklas 267     return spFactory.allCreated();
4437 31 Mar 17 nicklas 268   }
4437 31 Mar 17 nicklas 269
3939 16 May 16 nicklas 270 }