extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/plugins/cmd/RnaInfo.java

Code
Comments
Other
Rev Date Author Line
6207 12 Apr 21 nicklas 1 package net.sf.basedb.reggie.plugins.cmd;
6207 12 Apr 21 nicklas 2
6207 12 Apr 21 nicklas 3 import java.util.Date;
6510 03 Dec 21 nicklas 4 import java.util.List;
6510 03 Dec 21 nicklas 5
6510 03 Dec 21 nicklas 6 import net.sf.basedb.core.DbControl;
6510 03 Dec 21 nicklas 7 import net.sf.basedb.core.Extract;
6510 03 Dec 21 nicklas 8 import net.sf.basedb.core.ItemQuery;
6207 12 Apr 21 nicklas 9 import net.sf.basedb.core.Protocol;
6510 03 Dec 21 nicklas 10 import net.sf.basedb.core.Type;
6207 12 Apr 21 nicklas 11 import net.sf.basedb.core.data.PlateCoordinate;
6510 03 Dec 21 nicklas 12 import net.sf.basedb.core.query.Annotations;
6510 03 Dec 21 nicklas 13 import net.sf.basedb.core.query.Expressions;
6510 03 Dec 21 nicklas 14 import net.sf.basedb.core.query.Hql;
6510 03 Dec 21 nicklas 15 import net.sf.basedb.core.query.Restrictions;
6510 03 Dec 21 nicklas 16 import net.sf.basedb.reggie.Reggie;
6510 03 Dec 21 nicklas 17 import net.sf.basedb.reggie.dao.Annotationtype;
6510 03 Dec 21 nicklas 18 import net.sf.basedb.reggie.dao.Subtype;
6510 03 Dec 21 nicklas 19 import net.sf.basedb.util.Coordinate;
6714 29 Apr 22 nicklas 20 import net.sf.basedb.util.Values;
6207 12 Apr 21 nicklas 21
6207 12 Apr 21 nicklas 22 /**
6207 12 Apr 21 nicklas 23   Holds all information about a RNA. Validation will
6207 12 Apr 21 nicklas 24   be done at construction and errors are reported to
6207 12 Apr 21 nicklas 25   the JsonSection. Check the 'valid' flag before using
6207 12 Apr 21 nicklas 26   the information.
6207 12 Apr 21 nicklas 27   
6207 12 Apr 21 nicklas 28   @since 4.32
6207 12 Apr 21 nicklas 29 */
6207 12 Apr 21 nicklas 30 public class RnaInfo 
6207 12 Apr 21 nicklas 31 {
7232 02 Jun 23 nicklas 32   public Extract mergeWith;
6711 27 Apr 22 nicklas 33   public String tubeLabel;
6207 12 Apr 21 nicklas 34   public Date qiacubeDate;
6207 12 Apr 21 nicklas 35   public Integer qiacubePos;
6510 03 Dec 21 nicklas 36   public String qiacubeRunId;
6207 12 Apr 21 nicklas 37   public Integer qiacubeRunNumber;
6207 12 Apr 21 nicklas 38   public String operator;
6510 03 Dec 21 nicklas 39   public Float rin;
6207 12 Apr 21 nicklas 40   public Float qubitConc;
6714 29 Apr 22 nicklas 41   public Float originalQuantity_ng;
6714 29 Apr 22 nicklas 42   public Float remainingQuantity_ng;
6714 29 Apr 22 nicklas 43   public Float usedVolumeFromLysate_ul;
6918 01 Dec 22 nicklas 44   public String storageLocation;
6207 12 Apr 21 nicklas 45
6207 12 Apr 21 nicklas 46   public Protocol protocol;
6207 12 Apr 21 nicklas 47   public boolean valid;
6207 12 Apr 21 nicklas 48   
6893 25 Nov 22 nicklas 49   public RnaInfo(JsonSection section, SpecimenInfo specimen, LysateInfo lysate, MainInfo main, ImportContext ctx)
6207 12 Apr 21 nicklas 50   {
6207 12 Apr 21 nicklas 51     if (section != null)
6207 12 Apr 21 nicklas 52     {
7232 02 Jun 23 nicklas 53       if (lysate != null && lysate.mergeWith != null) 
7232 02 Jun 23 nicklas 54       {
7232 02 Jun 23 nicklas 55         mergeWith = findRNAForMerge(lysate, section);
7232 02 Jun 23 nicklas 56       }
7232 02 Jun 23 nicklas 57       
6732 05 May 22 nicklas 58       tubeLabel = section.getOptionalEntry("Specimen-ID", NullValidator.warnIfNull(PatternValidator.SPECIMEN_ID.withPrefixSuffix(specimen != null?specimen.tubeLabel:null, "-RNA")));
6893 25 Nov 22 nicklas 59       qiacubeDate = section.getRequiredEntry("Qiacube date", DateValidator.YYYY_MM_DD.warnIfFutureOrOlder(lysate!=null?lysate.partitionDate:null, main.refDate));
6207 12 Apr 21 nicklas 60       PlateCoordinate tmp = section.getRequiredEntry("Qiacube position", PlateWellValidator.QIACUBE);
6207 12 Apr 21 nicklas 61       if (tmp != null) qiacubePos = 6*tmp.getColumn()+tmp.getRow()+1;
6212 14 Apr 21 nicklas 62       operator = section.getOptionalEntry("Qiacube operator", null);
6207 12 Apr 21 nicklas 63       
6510 03 Dec 21 nicklas 64       qiacubeRunId = section.getRequiredEntry("Qiacube run number", PatternValidator.CMD_ID);
6510 03 Dec 21 nicklas 65       if (qiacubeRunId != null && qiacubePos != null && ctx != null)
6510 03 Dec 21 nicklas 66       {
6510 03 Dec 21 nicklas 67         JsonSection duplicate = ctx.add("Qiacube:"+qiacubeRunId+":"+qiacubePos, section);
6510 03 Dec 21 nicklas 68         if (duplicate != null)
6510 03 Dec 21 nicklas 69         {
6510 03 Dec 21 nicklas 70           String coordinate = (tmp.getRow()+1)+":"+Coordinate.numericToAlpha(tmp.getColumn()+1);
6510 03 Dec 21 nicklas 71           String msg = "Qiacube position ["+coordinate+"] duplicated in file: ";
6510 03 Dec 21 nicklas 72           section.addErrorMessage(msg+duplicate.getFile().getName());
6510 03 Dec 21 nicklas 73           duplicate.addErrorMessage(msg+section.getFile().getName());
6510 03 Dec 21 nicklas 74         }
6510 03 Dec 21 nicklas 75       }
6510 03 Dec 21 nicklas 76       if (qiacubeDate != null && qiacubePos != null && qiacubeRunId != null)
6510 03 Dec 21 nicklas 77       {
6510 03 Dec 21 nicklas 78         qiacubeRunNumber = findQiacubeRunNumber(qiacubeDate, qiacubeRunId, qiacubePos, section);
6510 03 Dec 21 nicklas 79       }
6510 03 Dec 21 nicklas 80       
6344 29 Jun 21 nicklas 81       qubitConc = section.getRequiredEntry("Concentration (ng/ul)", FloatValidator.POSITIVE);
6510 03 Dec 21 nicklas 82       rin = section.getRequiredEntry("RIN", FloatValidator.RIN);
6221 23 Apr 21 nicklas 83       protocol = section.getRequiredEntry("Protocol", ProtocolValidator.EXTRACTION_PROTOCOL);
6714 29 Apr 22 nicklas 84       originalQuantity_ng = section.getOptionalEntry("Original quantity (ng)", NullValidator.warnIfNull(FloatValidator.POSITIVE));
6714 29 Apr 22 nicklas 85       remainingQuantity_ng = section.getOptionalEntry("Remaining quantity (ng)", NullValidator.warnIfNull(FloatValidator.POSITIVE));
6946 06 Dec 22 nicklas 86       if (originalQuantity_ng != null && remainingQuantity_ng != null)
6714 29 Apr 22 nicklas 87       {
6946 06 Dec 22 nicklas 88         float usedQuantity_ng = originalQuantity_ng - remainingQuantity_ng;
6946 06 Dec 22 nicklas 89         if (usedQuantity_ng < 100)
6946 06 Dec 22 nicklas 90         {
6946 06 Dec 22 nicklas 91           // The used quantity is maybe a volume in µl. Test this with help of the qubitConc
6946 06 Dec 22 nicklas 92           float actualUsedQuantity_ng = usedQuantity_ng * qubitConc;
6946 06 Dec 22 nicklas 93           if (actualUsedQuantity_ng > usedQuantity_ng && actualUsedQuantity_ng <= originalQuantity_ng)
6946 06 Dec 22 nicklas 94           {
6946 06 Dec 22 nicklas 95             // Adjust
6946 06 Dec 22 nicklas 96             remainingQuantity_ng = originalQuantity_ng-actualUsedQuantity_ng;
6946 06 Dec 22 nicklas 97             section.addWarningMessage("Adjusting RNA.Used quantity from " + Values.formatNumber(usedQuantity_ng, 1, "ng") 
6946 06 Dec 22 nicklas 98               + " to " + Values.formatNumber(actualUsedQuantity_ng, 1, "ng"));
6946 06 Dec 22 nicklas 99           }
6946 06 Dec 22 nicklas 100           else
6946 06 Dec 22 nicklas 101           {
6946 06 Dec 22 nicklas 102             section.addWarningMessage("RNA.Used quantity < 100ng: "+Values.formatNumber(usedQuantity_ng, 1, "ng"));
6946 06 Dec 22 nicklas 103           }
6946 06 Dec 22 nicklas 104         }
6714 29 Apr 22 nicklas 105       }
6714 29 Apr 22 nicklas 106       if (lysate != null)
6714 29 Apr 22 nicklas 107       {
6714 29 Apr 22 nicklas 108         usedVolumeFromLysate_ul = lysate.usedVolumePerChildItem_ul;
6714 29 Apr 22 nicklas 109       }
6510 03 Dec 21 nicklas 110
6918 01 Dec 22 nicklas 111       // TODO -- maybe this will change in the future
6918 01 Dec 22 nicklas 112       storageLocation = section.getOptionalEntry("Storage location", NullValidator.allowNull(String.class));
6918 01 Dec 22 nicklas 113       
6207 12 Apr 21 nicklas 114     }
6207 12 Apr 21 nicklas 115     valid = section != null && !section.hasError();
6207 12 Apr 21 nicklas 116   }
6207 12 Apr 21 nicklas 117   
6510 03 Dec 21 nicklas 118   /**
6510 03 Dec 21 nicklas 119     Find the Qiacube run number. We will look for existing RNA items on the given
6510 03 Dec 21 nicklas 120     date. If we find a match with the same QiacubeRunId we use that same run number.
6510 03 Dec 21 nicklas 121     But! If the match happens to be on the same position and error is triggered.
6510 03 Dec 21 nicklas 122     If we find RNA on the same date with no or different QiacubeRunId we use
6510 03 Dec 21 nicklas 123     the max existing run number +1. If not matched RNA is found, the run number is 1.
6510 03 Dec 21 nicklas 124   */
6510 03 Dec 21 nicklas 125   private Integer findQiacubeRunNumber(Date qiacubeDate, String qiacubeRunId, Integer qiacubePos, JsonSection section)
6510 03 Dec 21 nicklas 126   {
6510 03 Dec 21 nicklas 127     ItemQuery<Extract> query = Extract.getQuery();
6510 03 Dec 21 nicklas 128     DbControl dc = section.getFile().dc();
6510 03 Dec 21 nicklas 129     query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
6510 03 Dec 21 nicklas 130     Subtype.RNA.addFilter(dc, query);
6510 03 Dec 21 nicklas 131     query.join(Annotations.innerJoin(null, Annotationtype.QIACUBE_DATE.load(dc), "qcdate"));
6510 03 Dec 21 nicklas 132     query.restrict(Restrictions.eq(Hql.alias("qcdate"), Expressions.parameter("qcdate", qiacubeDate, Type.DATE)));
6510 03 Dec 21 nicklas 133     
6510 03 Dec 21 nicklas 134     List<Extract> list = query.list(dc);    
6510 03 Dec 21 nicklas 135     Integer runNumberSameId = null;
6510 03 Dec 21 nicklas 136     Integer runNumberSameDate = null;
6510 03 Dec 21 nicklas 137     for (Extract rna : list)
6510 03 Dec 21 nicklas 138     {
6510 03 Dec 21 nicklas 139       String runId = (String)Annotationtype.QIACUBE_RUN_ID.getAnnotationValue(dc, rna);
6510 03 Dec 21 nicklas 140       Integer pos = (Integer)Annotationtype.QIACUBE_POSITION.getAnnotationValue(dc, rna);
6510 03 Dec 21 nicklas 141       Integer runNumber = (Integer)Annotationtype.QIACUBE_RUN_NO.getAnnotationValue(dc, rna);
6510 03 Dec 21 nicklas 142       if (pos == null || runNumber == null) continue; // Should not happen if everything is correctly registered!
6510 03 Dec 21 nicklas 143       
6510 03 Dec 21 nicklas 144       if (qiacubeRunId.equals(runId))
6510 03 Dec 21 nicklas 145       {
6510 03 Dec 21 nicklas 146         runNumberSameId = runNumber;
6510 03 Dec 21 nicklas 147         if (qiacubePos.equals(pos))
6510 03 Dec 21 nicklas 148         {
6510 03 Dec 21 nicklas 149           section.addErrorMessage("Found existing RNA on Qiacube position "+pos+": "+rna.getName());
6510 03 Dec 21 nicklas 150         }
6510 03 Dec 21 nicklas 151       }
6510 03 Dec 21 nicklas 152       else
6510 03 Dec 21 nicklas 153       {
6510 03 Dec 21 nicklas 154         if (runNumberSameDate == null || runNumber > runNumberSameDate)
6510 03 Dec 21 nicklas 155         {
6510 03 Dec 21 nicklas 156           runNumberSameDate = runNumber;
6510 03 Dec 21 nicklas 157         }
6510 03 Dec 21 nicklas 158       }
6510 03 Dec 21 nicklas 159     }
6510 03 Dec 21 nicklas 160     return runNumberSameId != null ? runNumberSameId : (runNumberSameDate != null ? runNumberSameDate+1 : 1);
6510 03 Dec 21 nicklas 161   }
7232 02 Jun 23 nicklas 162   
7232 02 Jun 23 nicklas 163   /**
7232 02 Jun 23 nicklas 164     If there is a Lysate to be merged, try to find a single child RNA
7232 02 Jun 23 nicklas 165     item that should also be merged.
7232 02 Jun 23 nicklas 166   */
7232 02 Jun 23 nicklas 167   private Extract findRNAForMerge(LysateInfo lysate, JsonSection section)
7232 02 Jun 23 nicklas 168   {
7232 02 Jun 23 nicklas 169     DbControl dc = section.getFile().dc();
7232 02 Jun 23 nicklas 170     ItemQuery<Extract> query = lysate.mergeWith.getChildExtracts();
7232 02 Jun 23 nicklas 171     query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
7232 02 Jun 23 nicklas 172     query.restrict(Subtype.RNA.restriction(dc, null));
7232 02 Jun 23 nicklas 173     
7232 02 Jun 23 nicklas 174     Extract merge = null;
7232 02 Jun 23 nicklas 175     List<Extract> extracts = query.list(dc);
7232 02 Jun 23 nicklas 176     if (extracts.size() == 1)
7232 02 Jun 23 nicklas 177     {
7232 02 Jun 23 nicklas 178       merge = extracts.get(0);
7232 02 Jun 23 nicklas 179     }
7232 02 Jun 23 nicklas 180     else if (extracts.size() == 0)
7232 02 Jun 23 nicklas 181     {
7232 02 Jun 23 nicklas 182       // TODO -- we could allow this without an error and create a new child RNA
7232 02 Jun 23 nicklas 183       // but there is no current use case for this scenario
7232 02 Jun 23 nicklas 184       section.addErrorMessage("Found a Specimen for merge but it has no child RNA: "+lysate.mergeWith.getName());
7232 02 Jun 23 nicklas 185     }
7232 02 Jun 23 nicklas 186     else
7232 02 Jun 23 nicklas 187     {
7232 02 Jun 23 nicklas 188       section.addErrorMessage("Found a Specimen for merge but there are "+extracts.size()+" child RNA: "+lysate.mergeWith.getName());
7232 02 Jun 23 nicklas 189     }
7232 02 Jun 23 nicklas 190     return merge;
7232 02 Jun 23 nicklas 191   }
7232 02 Jun 23 nicklas 192
6207 12 Apr 21 nicklas 193 }