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 |
Holds all information about a Specimen. Validation will |
6217 |
19 Apr 21 |
nicklas |
be done at construction and errors are reported to |
6217 |
19 Apr 21 |
nicklas |
the JsonSection. Check the 'valid' flag before using |
6217 |
19 Apr 21 |
nicklas |
the information. |
6217 |
19 Apr 21 |
nicklas |
27 |
|
6217 |
19 Apr 21 |
nicklas |
@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 |
// 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 |
// The 'biopsyTypeInJson' value may be translated so we get what is actually in the JSON |
6967 |
10 Jan 23 |
nicklas |
// 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 |
// 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 |
// 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 |
// 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 |
Check if there are any other Specimen or NoSpecimen items with the same Clarity ID. |
7229 |
02 Jun 23 |
nicklas |
This is normally an error condition, but if the specimen is member of the |
7229 |
02 Jun 23 |
nicklas |
Flagged External Samples list we allow information from the JSON to be |
7229 |
02 Jun 23 |
nicklas |
merged with the existing Specimen since it is an items that was created as part |
7229 |
02 Jun 23 |
nicklas |
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 |
Check if there are any other Specimen or NoSpecimen items with the same PAD |
7261 |
16 Jun 23 |
nicklas |
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 |
} |