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

Code
Comments
Other
Rev Date Author Line
6217 19 Apr 21 nicklas 1 package net.sf.basedb.reggie.plugins.cmd;
6217 19 Apr 21 nicklas 2
6584 11 Feb 22 nicklas 3 import java.util.List;
6341 29 Jun 21 nicklas 4
6584 11 Feb 22 nicklas 5 import net.sf.basedb.core.DbControl;
6584 11 Feb 22 nicklas 6 import net.sf.basedb.core.ItemQuery;
6584 11 Feb 22 nicklas 7 import net.sf.basedb.core.Sample;
6584 11 Feb 22 nicklas 8 import net.sf.basedb.core.query.Annotations;
6584 11 Feb 22 nicklas 9 import net.sf.basedb.core.query.Expressions;
6584 11 Feb 22 nicklas 10 import net.sf.basedb.core.query.Hql;
6584 11 Feb 22 nicklas 11 import net.sf.basedb.core.query.Restrictions;
6584 11 Feb 22 nicklas 12 import net.sf.basedb.reggie.Reggie;
6573 07 Feb 22 nicklas 13 import net.sf.basedb.reggie.dao.Annotationtype;
7229 02 Jun 23 nicklas 14 import net.sf.basedb.reggie.dao.BiomaterialList;
7229 02 Jun 23 nicklas 15 import net.sf.basedb.reggie.dao.SpecimenTube;
6584 11 Feb 22 nicklas 16 import net.sf.basedb.reggie.dao.Subtype;
6975 13 Jan 23 nicklas 17 import net.sf.basedb.reggie.plugins.cmd.CopyFromSample.MessageLevel;
6341 29 Jun 21 nicklas 18 import net.sf.basedb.reggie.plugins.cmd.ScanBIdRef.SampleIdType;
6217 19 Apr 21 nicklas 19 import net.sf.basedb.util.Values;
6584 11 Feb 22 nicklas 20 import net.sf.basedb.util.formatter.NameableFormatter;
6217 19 Apr 21 nicklas 21
6217 19 Apr 21 nicklas 22 /**
6217 19 Apr 21 nicklas 23   Holds all information about a Specimen. Validation will
6217 19 Apr 21 nicklas 24   be done at construction and errors are reported to
6217 19 Apr 21 nicklas 25   the JsonSection. Check the 'valid' flag before using
6217 19 Apr 21 nicklas 26   the information.
6217 19 Apr 21 nicklas 27   
6217 19 Apr 21 nicklas 28   @since 4.32
6217 19 Apr 21 nicklas 29 */
6217 19 Apr 21 nicklas 30 public class SpecimenInfo 
6217 19 Apr 21 nicklas 31 {
7232 02 Jun 23 nicklas 32   public Sample mergeWith;
7232 02 Jun 23 nicklas 33   
6217 19 Apr 21 nicklas 34   public String pad;
6993 20 Jan 23 nicklas 35   public String alternatePad;
6994 20 Jan 23 nicklas 36   public String operator;
6510 03 Dec 21 nicklas 37   public String clarityId;
6711 27 Apr 22 nicklas 38   public String tubeLabel;
6918 01 Dec 22 nicklas 39   public String storageLocation;
6217 19 Apr 21 nicklas 40   
6714 29 Apr 22 nicklas 41   public Float originalQuantity_mg;
6714 29 Apr 22 nicklas 42   public Float remainingQuantity_mg;
6217 19 Apr 21 nicklas 43
6217 19 Apr 21 nicklas 44   public String laterality;
6217 19 Apr 21 nicklas 45   public String specimenType;
6217 19 Apr 21 nicklas 46   public String biopsyType;
6217 19 Apr 21 nicklas 47   
6217 19 Apr 21 nicklas 48   public Integer numberOfTubes;
6217 19 Apr 21 nicklas 49   public Integer numberOfPieces;
6217 19 Apr 21 nicklas 50   
6217 19 Apr 21 nicklas 51   public String pathologyNote;
6217 19 Apr 21 nicklas 52   public String deliveryComment;
6217 19 Apr 21 nicklas 53   
6217 19 Apr 21 nicklas 54   public boolean valid;
6217 19 Apr 21 nicklas 55   
6577 09 Feb 22 nicklas 56   public SpecimenInfo(JsonSection section, MainInfo main, ImportContext ctx)
6217 19 Apr 21 nicklas 57   {
6217 19 Apr 21 nicklas 58     if (section != null)
6217 19 Apr 21 nicklas 59     {
6458 01 Nov 21 nicklas 60       if (main.idRef != null && main.idRef.idType == SampleIdType.CASE)
6341 29 Jun 21 nicklas 61       {
6510 03 Dec 21 nicklas 62         clarityId = section.getRequiredEntry("Clarity ID", PatternValidator.CLARITY_ID);
6577 09 Feb 22 nicklas 63         if (clarityId != null && ctx != null)
6577 09 Feb 22 nicklas 64         {
6577 09 Feb 22 nicklas 65           JsonSection duplicate = ctx.add("ClarityID:"+clarityId, section);
6577 09 Feb 22 nicklas 66           if (duplicate != null)
6577 09 Feb 22 nicklas 67           {
6577 09 Feb 22 nicklas 68             String msg = "Clarity ID ["+clarityId+"] duplicated in file: ";
6577 09 Feb 22 nicklas 69             section.addErrorMessage(msg+duplicate.getFile().getName());
6577 09 Feb 22 nicklas 70             duplicate.addErrorMessage(msg+section.getFile().getName());
6577 09 Feb 22 nicklas 71           }
6577 09 Feb 22 nicklas 72         }
7229 02 Jun 23 nicklas 73         if (clarityId != null)
7229 02 Jun 23 nicklas 74         {
7232 02 Jun 23 nicklas 75           mergeWith = verifyUniqueClarityId(clarityId, section);
7232 02 Jun 23 nicklas 76           if (mergeWith != null)
7229 02 Jun 23 nicklas 77           {
7232 02 Jun 23 nicklas 78             main.idRef.mergeWith = SpecimenTube.get(mergeWith);
7229 02 Jun 23 nicklas 79             main.idRef.copyMissingDataFrom = null;
7229 02 Jun 23 nicklas 80             main.idRef.noSpecimen = null;
7229 02 Jun 23 nicklas 81           }
7229 02 Jun 23 nicklas 82         }
7229 02 Jun 23 nicklas 83         
7229 02 Jun 23 nicklas 84         CopyFromSample cp = new CopyFromSample(main.idRef.copyMissingDataFrom);
7229 02 Jun 23 nicklas 85         boolean isCopyingFromNoSpecimen = main.idRef.noSpecimen != null;
7229 02 Jun 23 nicklas 86         
7229 02 Jun 23 nicklas 87         // We use NullValidator.allowNull() in some cases here since we currently don't get values in the files
7229 02 Jun 23 nicklas 88         laterality = section.getOptionalEntry("Laterality", 
7229 02 Jun 23 nicklas 89           cp.copyAnnotation(Annotationtype.LATERALITY, NullValidator.allowNull(EnumValidator.LATERALITY), 
7229 02 Jun 23 nicklas 90               MessageLevel.NONE, MessageLevel.ERROR, false, false));
7229 02 Jun 23 nicklas 91         pad = section.getOptionalEntry("PAD", 
7229 02 Jun 23 nicklas 92           cp.copyAnnotation(Annotationtype.PAD, NullValidator.warnIfNull(PatternValidator.PAD), 
7229 02 Jun 23 nicklas 93               MessageLevel.WARN, isCopyingFromNoSpecimen ? MessageLevel.ERROR : MessageLevel.WARN, true, isCopyingFromNoSpecimen));
6732 05 May 22 nicklas 94         tubeLabel = section.getOptionalEntry("Specimen-ID", NullValidator.warnIfNull(PatternValidator.SPECIMEN_ID));
6577 09 Feb 22 nicklas 95
7261 16 Jun 23 nicklas 96         if (pad != null && main.idRef.idType == SampleIdType.CASE)
6923 02 Dec 22 nicklas 97         {
7261 16 Jun 23 nicklas 98           verifyUniquePAD(pad, section, main.idRef.caseId);
6923 02 Dec 22 nicklas 99         }
6923 02 Dec 22 nicklas 100         
6975 13 Jan 23 nicklas 101         numberOfTubes = section.getOptionalEntry("Number of tubes", NullValidator.allowNull(IntValidator.POSITIVE.warnIf(1, 4)));
6581 10 Feb 22 nicklas 102         
6573 07 Feb 22 nicklas 103         numberOfPieces = section.getOptionalEntry("Number of pieces", NullValidator.warnIfNull(IntValidator.POSITIVE.warnIf(1, 10)));
6967 10 Jan 23 nicklas 104         if (main.idRef.id.endsWith("C")) 
6510 03 Dec 21 nicklas 105         {
6967 10 Jan 23 nicklas 106           biopsyType = "SpecimenCoreBiopsy";
6967 10 Jan 23 nicklas 107         }
6967 10 Jan 23 nicklas 108         else if (main.idRef.id.endsWith("D"))
6967 10 Jan 23 nicklas 109         {
6967 10 Jan 23 nicklas 110           biopsyType = "SpecimenCoreBiopsy2nd";
6967 10 Jan 23 nicklas 111         }
6967 10 Jan 23 nicklas 112         String biopsyTypeInJson = section.getOptionalEntry("Sample Type", EnumValidator.BIOPSY_TYPE.withTranslations(EnumValidator.BIOPSY_TYPE_TRANSLATIONS));
6967 10 Jan 23 nicklas 113         if (biopsyType != null && biopsyTypeInJson != null)
6967 10 Jan 23 nicklas 114         {
6967 10 Jan 23 nicklas 115           if (!biopsyType.equals(biopsyTypeInJson))
6510 03 Dec 21 nicklas 116           {
6967 10 Jan 23 nicklas 117             // The 'biopsyTypeInJson' value may be translated so we get what is actually in the JSON
6967 10 Jan 23 nicklas 118             // to avoid a confusing message
6967 10 Jan 23 nicklas 119             String rawValue = section.getOptionalEntry("Sample Type", null);
6967 10 Jan 23 nicklas 120             section.addWarningMessage("Specimen.Sample Type ("+rawValue+") doesn't match SCANB_ID suffix: "+main.idRef.id + " ("+biopsyType+")");
6510 03 Dec 21 nicklas 121           }
6510 03 Dec 21 nicklas 122         }
6967 10 Jan 23 nicklas 123         else if (biopsyType == null)
6967 10 Jan 23 nicklas 124         {
6967 10 Jan 23 nicklas 125           biopsyType = biopsyTypeInJson;
6967 10 Jan 23 nicklas 126         }
6510 03 Dec 21 nicklas 127         specimenType = section.getOptionalEntry("Specimen type", EnumValidator.SPECIMEN_TYPE);
6510 03 Dec 21 nicklas 128
6975 13 Jan 23 nicklas 129         pathologyNote = section.getOptionalEntry("Sample description", cp.copyAnnotation(Annotationtype.OTHER_PATH_NOTE, null, 
6975 13 Jan 23 nicklas 130             MessageLevel.NONE, isCopyingFromNoSpecimen ? MessageLevel.WARN : MessageLevel.NONE, false, isCopyingFromNoSpecimen));
6719 02 May 22 nicklas 131         deliveryComment = section.getOptionalEntry("Sample comments", null);
6993 20 Jan 23 nicklas 132         
6993 20 Jan 23 nicklas 133         // If the note matches the PAD validate we consider this as the alternate PAD (until we get a specific field in the JSON for it)
6993 20 Jan 23 nicklas 134         if (PatternValidator.PAD.matches(pathologyNote))
6993 20 Jan 23 nicklas 135         {
6993 20 Jan 23 nicklas 136           alternatePad = pathologyNote;
6993 20 Jan 23 nicklas 137         }
6341 29 Jun 21 nicklas 138       }
6217 19 Apr 21 nicklas 139       
6714 29 Apr 22 nicklas 140       originalQuantity_mg = section.getOptionalEntry("Original quantity (mg)", NullValidator.warnIfNull(FloatValidator.POSITIVE));
6955 12 Dec 22 nicklas 141       remainingQuantity_mg = section.getOptionalEntry("Remaining quantity (mg)", NullValidator.warnIfNull(FloatValidator.INSTANCE));
6217 19 Apr 21 nicklas 142       
6918 01 Dec 22 nicklas 143       // TODO -- maybe this will change in the future
6918 01 Dec 22 nicklas 144       storageLocation = section.getOptionalEntry("Storage location", NullValidator.allowNull(String.class));
6510 03 Dec 21 nicklas 145
6719 02 May 22 nicklas 146       // TODO -- check against existing specimen and warn if values differ?
6714 29 Apr 22 nicklas 147       if (originalQuantity_mg != null && remainingQuantity_mg != null && remainingQuantity_mg+0.1 > originalQuantity_mg)
6217 19 Apr 21 nicklas 148       {
6916 01 Dec 22 nicklas 149         section.addWarningMessage("Specimen.Remaining quantity ("+Values.formatNumber(remainingQuantity_mg, 1, "mg")+") > "
6714 29 Apr 22 nicklas 150           + "Original quantity ("+Values.formatNumber(originalQuantity_mg, 1, "mg")+")");
6217 19 Apr 21 nicklas 151       }
6217 19 Apr 21 nicklas 152     }
6217 19 Apr 21 nicklas 153     valid = section != null && !section.hasError();
6217 19 Apr 21 nicklas 154   }
6217 19 Apr 21 nicklas 155   
6584 11 Feb 22 nicklas 156   /**
6923 02 Dec 22 nicklas 157     Check if there are any other Specimen or NoSpecimen items with the same Clarity ID.
7229 02 Jun 23 nicklas 158     This is normally an error condition, but if the specimen is member of the
7229 02 Jun 23 nicklas 159     Flagged External Samples list we allow information from the JSON to be
7229 02 Jun 23 nicklas 160     merged with the existing Specimen since it is an items that was created as part
7229 02 Jun 23 nicklas 161     of a transfer of biomaterials.
6584 11 Feb 22 nicklas 162   */
7229 02 Jun 23 nicklas 163   private Sample verifyUniqueClarityId(String clarityId, JsonSection section)
6584 11 Feb 22 nicklas 164   {
6584 11 Feb 22 nicklas 165     DbControl dc = section.getFile().dc();
6584 11 Feb 22 nicklas 166     ItemQuery<Sample> query = Sample.getQuery();
6923 02 Dec 22 nicklas 167     query.restrict(Restrictions.or(
6923 02 Dec 22 nicklas 168       Subtype.SPECIMEN.restriction(dc, null),
6923 02 Dec 22 nicklas 169       Subtype.NO_SPECIMEN.restriction(dc, null)
6923 02 Dec 22 nicklas 170     ));
6584 11 Feb 22 nicklas 171     query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
6584 11 Feb 22 nicklas 172     query.join(Annotations.innerJoin(Annotationtype.EXTERNAL_REF.get(dc), "cid"));
6584 11 Feb 22 nicklas 173     query.restrict(Restrictions.eq(Hql.alias("cid"), Expressions.string(clarityId)));
6584 11 Feb 22 nicklas 174     
7229 02 Jun 23 nicklas 175     Sample merge = null;
6584 11 Feb 22 nicklas 176     List<Sample> samples = query.list(dc);
7229 02 Jun 23 nicklas 177     if (samples.size() == 1)
6584 11 Feb 22 nicklas 178     {
7229 02 Jun 23 nicklas 179       Sample s = samples.get(0);
7229 02 Jun 23 nicklas 180       if (BiomaterialList.FLAGGED_EXERNAL_SPECIMEN.get(dc).isMember(s))
7229 02 Jun 23 nicklas 181       {
7229 02 Jun 23 nicklas 182         merge = s;
7229 02 Jun 23 nicklas 183       }
7229 02 Jun 23 nicklas 184     }
7229 02 Jun 23 nicklas 185     if (merge == null && samples.size() > 0)
7229 02 Jun 23 nicklas 186     {
6923 02 Dec 22 nicklas 187       section.addErrorMessage("Found other Specimen/NoSpecimen with Clarity ID="+clarityId+ 
6584 11 Feb 22 nicklas 188         ": " + Values.getString(samples, ", ", true, new NameableFormatter()));
6584 11 Feb 22 nicklas 189     }
7229 02 Jun 23 nicklas 190     return merge;
6584 11 Feb 22 nicklas 191   }
6923 02 Dec 22 nicklas 192   
6923 02 Dec 22 nicklas 193   /**
7261 16 Jun 23 nicklas 194     Check if there are any other Specimen or NoSpecimen items with the same PAD
7261 16 Jun 23 nicklas 195     that doesn't have the same SCAN-B id.
6923 02 Dec 22 nicklas 196   */
7261 16 Jun 23 nicklas 197   private void verifyUniquePAD(String pad, JsonSection section, String ignoreCase)
6923 02 Dec 22 nicklas 198   {
6923 02 Dec 22 nicklas 199     DbControl dc = section.getFile().dc();
6923 02 Dec 22 nicklas 200     ItemQuery<Sample> query = Sample.getQuery();
6923 02 Dec 22 nicklas 201     query.restrict(Restrictions.or(
6923 02 Dec 22 nicklas 202         Subtype.SPECIMEN.restriction(dc, null),
6923 02 Dec 22 nicklas 203         Subtype.NO_SPECIMEN.restriction(dc, null)
6923 02 Dec 22 nicklas 204       ));
6923 02 Dec 22 nicklas 205     query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
6923 02 Dec 22 nicklas 206     query.join(Annotations.innerJoin(Annotationtype.PAD.get(dc), "pad"));
6923 02 Dec 22 nicklas 207     query.restrict(Restrictions.eq(Hql.alias("pad"), Expressions.string(pad)));
7261 16 Jun 23 nicklas 208     if (ignoreCase != null)
6923 02 Dec 22 nicklas 209     {
7261 16 Jun 23 nicklas 210       query.restrict(Restrictions.not(Restrictions.like(Hql.property("name"), Expressions.string(ignoreCase+"%"))));
6923 02 Dec 22 nicklas 211     }
6923 02 Dec 22 nicklas 212     
6923 02 Dec 22 nicklas 213     List<Sample> samples = query.list(dc);
6923 02 Dec 22 nicklas 214     if (samples.size() > 0)
6923 02 Dec 22 nicklas 215     {
6923 02 Dec 22 nicklas 216       section.addErrorMessage("Found other Specimen/NoSpecimen with PAD="+pad+ 
6923 02 Dec 22 nicklas 217         ": " + Values.getString(samples, ", ", true, new NameableFormatter()));
6923 02 Dec 22 nicklas 218     }
6923 02 Dec 22 nicklas 219   }
6584 11 Feb 22 nicklas 220
6217 19 Apr 21 nicklas 221 }