6342 |
29 Jun 21 |
nicklas |
1 |
package net.sf.basedb.reggie.plugins.cmd; |
6342 |
29 Jun 21 |
nicklas |
2 |
|
6893 |
25 Nov 22 |
nicklas |
3 |
import java.util.Date; |
6573 |
07 Feb 22 |
nicklas |
4 |
import java.util.List; |
6573 |
07 Feb 22 |
nicklas |
5 |
|
6342 |
29 Jun 21 |
nicklas |
6 |
import net.sf.basedb.core.DbControl; |
6510 |
03 Dec 21 |
nicklas |
7 |
import net.sf.basedb.core.PermissionDeniedException; |
6581 |
10 Feb 22 |
nicklas |
8 |
import net.sf.basedb.core.Sample; |
7251 |
08 Jun 23 |
nicklas |
9 |
import net.sf.basedb.reggie.Site; |
6583 |
11 Feb 22 |
nicklas |
10 |
import net.sf.basedb.reggie.dao.Annotationtype; |
6510 |
03 Dec 21 |
nicklas |
11 |
import net.sf.basedb.reggie.dao.Case; |
6573 |
07 Feb 22 |
nicklas |
12 |
import net.sf.basedb.reggie.dao.NoSpecimen; |
6342 |
29 Jun 21 |
nicklas |
13 |
import net.sf.basedb.reggie.dao.Rna; |
6342 |
29 Jun 21 |
nicklas |
14 |
import net.sf.basedb.reggie.dao.SpecimenTube; |
6342 |
29 Jun 21 |
nicklas |
15 |
|
6342 |
29 Jun 21 |
nicklas |
16 |
/** |
6342 |
29 Jun 21 |
nicklas |
Validator for the SCAN-B ID found in the JSON file. We |
6342 |
29 Jun 21 |
nicklas |
support 3 types of references: |
6342 |
29 Jun 21 |
nicklas |
19 |
|
6342 |
29 Jun 21 |
nicklas |
* Prenormalized RNA: A pre-normalized RNA with the exact name should already exist |
6342 |
29 Jun 21 |
nicklas |
and it shoult not have existing child items. |
6342 |
29 Jun 21 |
nicklas |
* Specimen: An existing specimen with the exact name should already exist. |
6510 |
03 Dec 21 |
nicklas |
* Case: A new specimen is created for the given case. The case may or may not already exist. |
6583 |
11 Feb 22 |
nicklas |
In this scenario we also support that an existing NoSpecimen item is used and converted |
6583 |
11 Feb 22 |
nicklas |
to a specimen item. |
6342 |
29 Jun 21 |
nicklas |
26 |
|
6342 |
29 Jun 21 |
nicklas |
@since 4.32 |
6342 |
29 Jun 21 |
nicklas |
28 |
*/ |
6342 |
29 Jun 21 |
nicklas |
29 |
public class ScanBIdRef |
6342 |
29 Jun 21 |
nicklas |
30 |
implements ValueValidator<String, ScanBIdRef> |
6342 |
29 Jun 21 |
nicklas |
31 |
{ |
6578 |
09 Feb 22 |
nicklas |
32 |
private final ImportContext ctx; |
6342 |
29 Jun 21 |
nicklas |
33 |
|
6342 |
29 Jun 21 |
nicklas |
34 |
public String id; |
7261 |
16 Jun 23 |
nicklas |
35 |
public String caseId; |
6342 |
29 Jun 21 |
nicklas |
36 |
public SampleIdType idType; |
6342 |
29 Jun 21 |
nicklas |
37 |
public Rna rna; |
6342 |
29 Jun 21 |
nicklas |
38 |
public SpecimenTube specimen; |
6573 |
07 Feb 22 |
nicklas |
39 |
public NoSpecimen noSpecimen; |
6581 |
10 Feb 22 |
nicklas |
40 |
public Sample copyMissingDataFrom; |
7229 |
02 Jun 23 |
nicklas |
41 |
public SpecimenTube mergeWith; |
6510 |
03 Dec 21 |
nicklas |
42 |
public Case theCase; |
6893 |
25 Nov 22 |
nicklas |
43 |
public Date refDate; |
6342 |
29 Jun 21 |
nicklas |
44 |
|
6578 |
09 Feb 22 |
nicklas |
45 |
public ScanBIdRef(ImportContext ctx) |
6578 |
09 Feb 22 |
nicklas |
46 |
{ |
6578 |
09 Feb 22 |
nicklas |
47 |
this.ctx = ctx; |
6578 |
09 Feb 22 |
nicklas |
48 |
} |
6342 |
29 Jun 21 |
nicklas |
49 |
|
6342 |
29 Jun 21 |
nicklas |
50 |
@Override |
6342 |
29 Jun 21 |
nicklas |
51 |
public Class<String> getExpectedClass() |
6342 |
29 Jun 21 |
nicklas |
52 |
{ |
6342 |
29 Jun 21 |
nicklas |
53 |
return String.class; |
6342 |
29 Jun 21 |
nicklas |
54 |
} |
6342 |
29 Jun 21 |
nicklas |
55 |
|
6342 |
29 Jun 21 |
nicklas |
56 |
@Override |
6342 |
29 Jun 21 |
nicklas |
57 |
public ScanBIdRef isValid(DbControl dc, String value, JsonSection section, String entryKey) |
6342 |
29 Jun 21 |
nicklas |
58 |
{ |
6342 |
29 Jun 21 |
nicklas |
59 |
if (value != null) |
6342 |
29 Jun 21 |
nicklas |
60 |
{ |
6342 |
29 Jun 21 |
nicklas |
// Convert '-' to '.' |
6902 |
29 Nov 22 |
nicklas |
62 |
value = value.replace('-', '.').strip(); |
6342 |
29 Jun 21 |
nicklas |
63 |
id = value; |
6342 |
29 Jun 21 |
nicklas |
64 |
|
7234 |
02 Jun 23 |
nicklas |
65 |
if (value.matches("\\d{7}(C|D|T)?")) |
6342 |
29 Jun 21 |
nicklas |
66 |
{ |
6510 |
03 Dec 21 |
nicklas |
// 7 digits is a SCAN-B CASE ID with optional 'C' or 'D' suffix |
6342 |
29 Jun 21 |
nicklas |
68 |
idType = SampleIdType.CASE; |
7261 |
16 Jun 23 |
nicklas |
69 |
caseId = id.substring(0, 7); |
7234 |
02 Jun 23 |
nicklas |
// 'T' is for the frozen piece and we should normally not see it |
7234 |
02 Jun 23 |
nicklas |
// but if it is here we simple remove it |
7261 |
16 Jun 23 |
nicklas |
72 |
if (id.endsWith("T")) id = caseId; |
6342 |
29 Jun 21 |
nicklas |
73 |
} |
6342 |
29 Jun 21 |
nicklas |
74 |
else if (value.matches("\\d{7}\\.\\d")) |
6342 |
29 Jun 21 |
nicklas |
75 |
{ |
6342 |
29 Jun 21 |
nicklas |
// 7 digits + a dot with a single digit |
6342 |
29 Jun 21 |
nicklas |
77 |
idType = SampleIdType.SPECIMEN; |
7261 |
16 Jun 23 |
nicklas |
78 |
caseId = id.substring(0, 7); |
6342 |
29 Jun 21 |
nicklas |
79 |
specimen = SpecimenTube.findByTubeName(dc, id); |
6342 |
29 Jun 21 |
nicklas |
80 |
if (specimen == null) |
6342 |
29 Jun 21 |
nicklas |
81 |
{ |
6342 |
29 Jun 21 |
nicklas |
82 |
section.addErrorMessage("Can't find Specimen with name: "+id); |
6342 |
29 Jun 21 |
nicklas |
83 |
} |
6342 |
29 Jun 21 |
nicklas |
84 |
} |
6342 |
29 Jun 21 |
nicklas |
85 |
else if (value.matches("\\d{7}\\..*n.*")) |
6342 |
29 Jun 21 |
nicklas |
86 |
{ |
6342 |
29 Jun 21 |
nicklas |
// If we find an 'n' in the name we assume it is a pre-normalized |
6342 |
29 Jun 21 |
nicklas |
88 |
idType = SampleIdType.PRENORMALISED_RNA; |
7261 |
16 Jun 23 |
nicklas |
89 |
caseId = id.substring(0, 7); |
6342 |
29 Jun 21 |
nicklas |
90 |
rna = Rna.findByName(dc, id, true); |
6342 |
29 Jun 21 |
nicklas |
91 |
if (rna == null) |
6342 |
29 Jun 21 |
nicklas |
92 |
{ |
6342 |
29 Jun 21 |
nicklas |
93 |
section.addErrorMessage("Can't find RNA with name: "+id); |
6342 |
29 Jun 21 |
nicklas |
94 |
} |
6342 |
29 Jun 21 |
nicklas |
95 |
else if (!rna.isPreNormalized(dc)) |
6342 |
29 Jun 21 |
nicklas |
96 |
{ |
6342 |
29 Jun 21 |
nicklas |
97 |
section.addWarningMessage("Expected a pre-normalized RNA: "+id); |
6342 |
29 Jun 21 |
nicklas |
98 |
} |
6342 |
29 Jun 21 |
nicklas |
99 |
else if (rna.getItem().countExtracts() > 0) |
6342 |
29 Jun 21 |
nicklas |
100 |
{ |
6342 |
29 Jun 21 |
nicklas |
101 |
section.addErrorMessage("The pre-normalized RNA ("+id+") already have a child library"); |
6342 |
29 Jun 21 |
nicklas |
102 |
} |
6342 |
29 Jun 21 |
nicklas |
103 |
} |
6342 |
29 Jun 21 |
nicklas |
104 |
else |
6342 |
29 Jun 21 |
nicklas |
105 |
{ |
6342 |
29 Jun 21 |
nicklas |
106 |
section.addErrorMessage("Invalid SCANB_ID: " + id); |
6342 |
29 Jun 21 |
nicklas |
107 |
} |
6510 |
03 Dec 21 |
nicklas |
108 |
|
6510 |
03 Dec 21 |
nicklas |
109 |
if (idType != null) |
6510 |
03 Dec 21 |
nicklas |
110 |
{ |
7251 |
08 Jun 23 |
nicklas |
// Check that the site prefix is a known site |
7251 |
08 Jun 23 |
nicklas |
112 |
if (Site.findByCaseName(id) == Site.UNKNOWN) |
7251 |
08 Jun 23 |
nicklas |
113 |
{ |
7251 |
08 Jun 23 |
nicklas |
114 |
section.addErrorMessage("SCANB_ID doesn't match a known site: " + id); |
7251 |
08 Jun 23 |
nicklas |
115 |
} |
7251 |
08 Jun 23 |
nicklas |
116 |
|
7251 |
08 Jun 23 |
nicklas |
// Check if a Case already exists (but in most cases it won't) |
6510 |
03 Dec 21 |
nicklas |
118 |
theCase = Case.findByName(dc, id); |
6510 |
03 Dec 21 |
nicklas |
119 |
if (theCase != null) |
6510 |
03 Dec 21 |
nicklas |
120 |
{ |
6510 |
03 Dec 21 |
nicklas |
121 |
try |
6510 |
03 Dec 21 |
nicklas |
122 |
{ |
6510 |
03 Dec 21 |
nicklas |
123 |
theCase.verifyConsent(dc, null); |
6510 |
03 Dec 21 |
nicklas |
124 |
} |
6510 |
03 Dec 21 |
nicklas |
125 |
catch (PermissionDeniedException ex) |
6510 |
03 Dec 21 |
nicklas |
126 |
{ |
6510 |
03 Dec 21 |
nicklas |
127 |
section.addErrorMessage(ex.getMessage()); |
6510 |
03 Dec 21 |
nicklas |
128 |
} |
6510 |
03 Dec 21 |
nicklas |
129 |
} |
6510 |
03 Dec 21 |
nicklas |
130 |
} |
6573 |
07 Feb 22 |
nicklas |
131 |
|
6573 |
07 Feb 22 |
nicklas |
132 |
if (idType == SampleIdType.CASE) |
6573 |
07 Feb 22 |
nicklas |
133 |
{ |
6573 |
07 Feb 22 |
nicklas |
// Check if we can find an existing NoSpecimen registration that |
6583 |
11 Feb 22 |
nicklas |
// is maybe a placeholder for specimen in this case |
6583 |
11 Feb 22 |
nicklas |
136 |
List<NoSpecimen> nsList = NoSpecimen.findByCaseName(dc, id, true); |
6583 |
11 Feb 22 |
nicklas |
// We can't use a single NoSpecimen for more than one JSON |
6583 |
11 Feb 22 |
nicklas |
138 |
for (NoSpecimen ns : nsList) |
6578 |
09 Feb 22 |
nicklas |
139 |
{ |
6583 |
11 Feb 22 |
nicklas |
140 |
copyMissingDataFrom = ns.getItem(); |
6583 |
11 Feb 22 |
nicklas |
141 |
JsonSection duplicate = null; |
6583 |
11 Feb 22 |
nicklas |
142 |
String specimenExists = (String)Annotationtype.EXTERNAL_SPECIMEN_EXISTS.getAnnotationValue(dc, ns.getItem()); |
6583 |
11 Feb 22 |
nicklas |
143 |
if ("Yes".equals(specimenExists)) |
6578 |
09 Feb 22 |
nicklas |
144 |
{ |
6578 |
09 Feb 22 |
nicklas |
145 |
if (ctx != null) |
6578 |
09 Feb 22 |
nicklas |
146 |
{ |
6578 |
09 Feb 22 |
nicklas |
147 |
duplicate = ctx.add("NoSpecimen:"+ns.getName(), section); |
6578 |
09 Feb 22 |
nicklas |
148 |
} |
6578 |
09 Feb 22 |
nicklas |
149 |
if (duplicate == null) |
6578 |
09 Feb 22 |
nicklas |
150 |
{ |
6578 |
09 Feb 22 |
nicklas |
151 |
noSpecimen = ns; |
6578 |
09 Feb 22 |
nicklas |
152 |
break; |
6578 |
09 Feb 22 |
nicklas |
153 |
} |
6578 |
09 Feb 22 |
nicklas |
154 |
} |
6578 |
09 Feb 22 |
nicklas |
155 |
} |
6583 |
11 Feb 22 |
nicklas |
156 |
if (noSpecimen == null) |
6583 |
11 Feb 22 |
nicklas |
157 |
{ |
6583 |
11 Feb 22 |
nicklas |
// If there is no matching NoSpecimen we try to see if it is possible to use |
6583 |
11 Feb 22 |
nicklas |
// another specimen to copy information from. But it must be a specimen |
6583 |
11 Feb 22 |
nicklas |
// that has been handled externally. |
6583 |
11 Feb 22 |
nicklas |
161 |
List<SpecimenTube> spList = SpecimenTube.findByCaseName(dc, id, true); |
6583 |
11 Feb 22 |
nicklas |
162 |
for (SpecimenTube sp : spList) |
6583 |
11 Feb 22 |
nicklas |
163 |
{ |
6583 |
11 Feb 22 |
nicklas |
164 |
if (Annotationtype.EXTERNAL_REF.getAnnotationValue(dc, sp.getItem()) != null) |
6583 |
11 Feb 22 |
nicklas |
165 |
{ |
6583 |
11 Feb 22 |
nicklas |
166 |
copyMissingDataFrom = sp.getItem(); |
6583 |
11 Feb 22 |
nicklas |
167 |
} |
6583 |
11 Feb 22 |
nicklas |
168 |
} |
6583 |
11 Feb 22 |
nicklas |
169 |
} |
6573 |
07 Feb 22 |
nicklas |
170 |
} |
6893 |
25 Nov 22 |
nicklas |
// Try to get a sensible reference date that we can compare other dates to |
6893 |
25 Nov 22 |
nicklas |
// Dates in the JSON should be after the refDate |
6893 |
25 Nov 22 |
nicklas |
173 |
if (copyMissingDataFrom != null) |
6893 |
25 Nov 22 |
nicklas |
174 |
{ |
6893 |
25 Nov 22 |
nicklas |
175 |
refDate = copyMissingDataFrom.getCreationEvent().getEventDate(); |
6893 |
25 Nov 22 |
nicklas |
176 |
} |
6342 |
29 Jun 21 |
nicklas |
177 |
} |
6342 |
29 Jun 21 |
nicklas |
178 |
return this; |
6342 |
29 Jun 21 |
nicklas |
179 |
} |
6342 |
29 Jun 21 |
nicklas |
180 |
|
6342 |
29 Jun 21 |
nicklas |
181 |
/** |
6342 |
29 Jun 21 |
nicklas |
The type of SCAN-B ID. |
6342 |
29 Jun 21 |
nicklas |
183 |
*/ |
6342 |
29 Jun 21 |
nicklas |
184 |
public static enum SampleIdType |
6342 |
29 Jun 21 |
nicklas |
185 |
{ |
6342 |
29 Jun 21 |
nicklas |
186 |
CASE, |
6342 |
29 Jun 21 |
nicklas |
187 |
SPECIMEN, |
6342 |
29 Jun 21 |
nicklas |
188 |
PRENORMALISED_RNA |
6342 |
29 Jun 21 |
nicklas |
189 |
} |
6342 |
29 Jun 21 |
nicklas |
190 |
|
6342 |
29 Jun 21 |
nicklas |
191 |
} |