2933 |
14 Nov 14 |
olle |
1 |
package net.sf.basedb.meludi.dao; |
2933 |
14 Nov 14 |
olle |
2 |
|
2933 |
14 Nov 14 |
olle |
3 |
import java.util.ArrayList; |
2933 |
14 Nov 14 |
olle |
4 |
import java.util.List; |
2933 |
14 Nov 14 |
olle |
5 |
import java.util.Set; |
2933 |
14 Nov 14 |
olle |
6 |
import java.util.TreeSet; |
2933 |
14 Nov 14 |
olle |
7 |
|
2933 |
14 Nov 14 |
olle |
8 |
import org.json.simple.JSONObject; |
2933 |
14 Nov 14 |
olle |
9 |
|
4860 |
19 Jun 18 |
olle |
10 |
import net.sf.basedb.core.AnnotationSimpleRestriction; |
4860 |
19 Jun 18 |
olle |
11 |
import net.sf.basedb.core.AnnotationType; |
4860 |
19 Jun 18 |
olle |
12 |
import net.sf.basedb.core.BioSource; |
2933 |
14 Nov 14 |
olle |
13 |
import net.sf.basedb.core.DbControl; |
2933 |
14 Nov 14 |
olle |
14 |
import net.sf.basedb.core.Include; |
2933 |
14 Nov 14 |
olle |
15 |
import net.sf.basedb.core.InvalidDataException; |
2933 |
14 Nov 14 |
olle |
16 |
import net.sf.basedb.core.ItemQuery; |
4860 |
19 Jun 18 |
olle |
17 |
import net.sf.basedb.core.Operator; |
2933 |
14 Nov 14 |
olle |
18 |
import net.sf.basedb.core.PermissionDeniedException; |
2933 |
14 Nov 14 |
olle |
19 |
import net.sf.basedb.core.Sample; |
2933 |
14 Nov 14 |
olle |
20 |
import net.sf.basedb.core.Type; |
2933 |
14 Nov 14 |
olle |
21 |
import net.sf.basedb.core.query.Expressions; |
2933 |
14 Nov 14 |
olle |
22 |
import net.sf.basedb.core.query.Hql; |
2933 |
14 Nov 14 |
olle |
23 |
import net.sf.basedb.core.query.Orders; |
2933 |
14 Nov 14 |
olle |
24 |
import net.sf.basedb.core.query.Restrictions; |
4149 |
03 Oct 16 |
olle |
25 |
import net.sf.basedb.meludi.Meludi; |
2933 |
14 Nov 14 |
olle |
26 |
|
2933 |
14 Nov 14 |
olle |
27 |
/** |
2933 |
14 Nov 14 |
olle |
Class for loading information that is related to cases. |
2933 |
14 Nov 14 |
olle |
29 |
|
2933 |
14 Nov 14 |
olle |
@author nicklas |
2933 |
14 Nov 14 |
olle |
@since 1.2 |
2933 |
14 Nov 14 |
olle |
32 |
*/ |
2933 |
14 Nov 14 |
olle |
33 |
public class Case |
2933 |
14 Nov 14 |
olle |
34 |
extends MeludiItem<Sample> |
2933 |
14 Nov 14 |
olle |
35 |
{ |
2933 |
14 Nov 14 |
olle |
36 |
|
2933 |
14 Nov 14 |
olle |
37 |
/** |
2933 |
14 Nov 14 |
olle |
Find a case by name. This method will first check for an exact match |
2933 |
14 Nov 14 |
olle |
on the name of a sample. If exactly one match is found this is the |
2933 |
14 Nov 14 |
olle |
(primary) case. More than one match is an error condition. If no |
2933 |
14 Nov 14 |
olle |
match is found, we look harder by trying to find a child sample with |
2933 |
14 Nov 14 |
olle |
'Specimen' or 'NoSpecimen' that have names matching the case we are looking |
2933 |
14 Nov 14 |
olle |
for (eg. xxx.1, xxx.2, etc.). If exactly one match is found this is |
2933 |
14 Nov 14 |
olle |
the (merged) case. More than one match is an error condition. No match |
2933 |
14 Nov 14 |
olle |
indicates a case that has not yet been registered (null is returned). |
2933 |
14 Nov 14 |
olle |
46 |
*/ |
2933 |
14 Nov 14 |
olle |
47 |
public static Case findByName(DbControl dc, String name) |
2933 |
14 Nov 14 |
olle |
48 |
{ |
2933 |
14 Nov 14 |
olle |
49 |
Case theCase = null; |
2933 |
14 Nov 14 |
olle |
50 |
boolean merged = false; |
2933 |
14 Nov 14 |
olle |
51 |
|
2933 |
14 Nov 14 |
olle |
// Get rid of suffixes in the name (eg. C/D which is used for pre-neoadjuvant forms) |
4149 |
03 Oct 16 |
olle |
53 |
name = Meludi.fetchRootItemName(name, dc.getSessionControl().getActiveProjectId()); |
2933 |
14 Nov 14 |
olle |
54 |
|
2933 |
14 Nov 14 |
olle |
// Look for a case with the given name (primary case) |
2933 |
14 Nov 14 |
olle |
56 |
ItemQuery<Sample> caseQuery = Sample.getQuery(); |
2933 |
14 Nov 14 |
olle |
57 |
Subtype.CASE.addFilter(dc, caseQuery); |
2933 |
14 Nov 14 |
olle |
58 |
caseQuery.restrict(Restrictions.eq(Hql.property("name"), Expressions.parameter("name", name, Type.STRING))); |
2933 |
14 Nov 14 |
olle |
59 |
List<Sample> cases = caseQuery.list(dc); |
2933 |
14 Nov 14 |
olle |
60 |
|
2933 |
14 Nov 14 |
olle |
// ...if more than one is found, something is incorrectly registered... abort |
2933 |
14 Nov 14 |
olle |
62 |
if (cases.size() > 1) |
2933 |
14 Nov 14 |
olle |
63 |
{ |
2933 |
14 Nov 14 |
olle |
64 |
throw new InvalidDataException( |
2933 |
14 Nov 14 |
olle |
65 |
"Found " + cases.size() + " cases with the same name (" + name + |
2933 |
14 Nov 14 |
olle |
66 |
"). This wizard can't be used until that is corrected."); |
2933 |
14 Nov 14 |
olle |
67 |
} |
2933 |
14 Nov 14 |
olle |
68 |
|
2933 |
14 Nov 14 |
olle |
69 |
if (cases.size() == 0) |
2933 |
14 Nov 14 |
olle |
70 |
{ |
2933 |
14 Nov 14 |
olle |
// Not found... see if we can find a different case |
2933 |
14 Nov 14 |
olle |
// with 'Specimen'/'NoSpecimen' child items belonging to this case (merged case) |
2933 |
14 Nov 14 |
olle |
73 |
merged = true; |
2933 |
14 Nov 14 |
olle |
74 |
caseQuery = Sample.getQuery(); |
2933 |
14 Nov 14 |
olle |
75 |
Subtype.CASE.addFilter(dc, caseQuery); |
2933 |
14 Nov 14 |
olle |
76 |
caseQuery.join(Hql.innerJoin("childCreationEvents", "cce")); |
2933 |
14 Nov 14 |
olle |
77 |
caseQuery.join(Hql.innerJoin("cce", "event", "evt")); |
2933 |
14 Nov 14 |
olle |
78 |
caseQuery.join(Hql.innerJoin("evt", "bioMaterial", "sp")); // 'sp' should now reference a specimen tube |
2933 |
14 Nov 14 |
olle |
79 |
|
2933 |
14 Nov 14 |
olle |
// Restrict to 'Specimen' child items |
2933 |
14 Nov 14 |
olle |
81 |
caseQuery.restrict(Restrictions.or(Subtype.SPECIMEN.restriction(dc, "sp"))); |
2933 |
14 Nov 14 |
olle |
82 |
caseQuery.restrict(Restrictions.like(Hql.property("sp", "name"), Expressions.parameter("name", name + ".%", Type.STRING))); |
2933 |
14 Nov 14 |
olle |
83 |
caseQuery.setDistinct(true); |
2933 |
14 Nov 14 |
olle |
84 |
|
2933 |
14 Nov 14 |
olle |
85 |
cases = caseQuery.list(dc); |
2933 |
14 Nov 14 |
olle |
86 |
|
2933 |
14 Nov 14 |
olle |
87 |
if (cases.size() > 1) |
2933 |
14 Nov 14 |
olle |
88 |
{ |
2933 |
14 Nov 14 |
olle |
// ...something is very incorrectly registered... |
2933 |
14 Nov 14 |
olle |
// we have specimen tubes xxx.1, xxx.2, etc. with different parents |
2933 |
14 Nov 14 |
olle |
91 |
throw new InvalidDataException( |
2933 |
14 Nov 14 |
olle |
92 |
"The specimen tubes for this case (" + name + ") are already linked with " + cases.size() + |
2933 |
14 Nov 14 |
olle |
93 |
" different cases. This wizard can't be used until that is corrected."); |
2933 |
14 Nov 14 |
olle |
94 |
} |
2933 |
14 Nov 14 |
olle |
95 |
|
2933 |
14 Nov 14 |
olle |
96 |
} |
2933 |
14 Nov 14 |
olle |
97 |
|
2933 |
14 Nov 14 |
olle |
98 |
if (cases.size() == 1) |
2933 |
14 Nov 14 |
olle |
99 |
{ |
2933 |
14 Nov 14 |
olle |
100 |
theCase = new Case(cases.get(0), name, merged); |
2933 |
14 Nov 14 |
olle |
101 |
} |
2933 |
14 Nov 14 |
olle |
102 |
|
2933 |
14 Nov 14 |
olle |
103 |
return theCase; |
2933 |
14 Nov 14 |
olle |
104 |
|
2933 |
14 Nov 14 |
olle |
105 |
} |
2933 |
14 Nov 14 |
olle |
106 |
|
2933 |
14 Nov 14 |
olle |
107 |
/** |
2933 |
14 Nov 14 |
olle |
Find all cases linked with a patient. A patient can have 0, 1 or 2 |
2933 |
14 Nov 14 |
olle |
cases. If more than two are found an exception is thrown. |
2933 |
14 Nov 14 |
olle |
The cases are sorted in ascending order after case name. |
2933 |
14 Nov 14 |
olle |
111 |
*/ |
2933 |
14 Nov 14 |
olle |
112 |
public static List<Case> findByPatient(DbControl dc, Patient patient) |
2933 |
14 Nov 14 |
olle |
113 |
{ |
2933 |
14 Nov 14 |
olle |
114 |
ItemQuery<Sample> caseQuery = patient.getBioSource().getSamples(); |
2933 |
14 Nov 14 |
olle |
115 |
Subtype.CASE.addFilter(dc, caseQuery); |
2933 |
14 Nov 14 |
olle |
116 |
caseQuery.include(Include.ALL); |
2933 |
14 Nov 14 |
olle |
117 |
caseQuery.order(Orders.asc(Hql.property("name"))); |
2933 |
14 Nov 14 |
olle |
118 |
List<Sample> tmp = caseQuery.list(dc); |
2933 |
14 Nov 14 |
olle |
119 |
if (tmp.size() > 2) |
2933 |
14 Nov 14 |
olle |
120 |
{ |
2933 |
14 Nov 14 |
olle |
121 |
throw new InvalidDataException("The patient (" + patient.getName() + |
2933 |
14 Nov 14 |
olle |
122 |
") has more than two cases associated with. " + |
2933 |
14 Nov 14 |
olle |
123 |
"This wizard can't be used until that is corrected."); |
2933 |
14 Nov 14 |
olle |
124 |
} |
2933 |
14 Nov 14 |
olle |
125 |
|
2933 |
14 Nov 14 |
olle |
126 |
List<Case> cases = new ArrayList<Case>(tmp.size()); |
2933 |
14 Nov 14 |
olle |
127 |
for (Sample s : caseQuery.list(dc)) |
2933 |
14 Nov 14 |
olle |
128 |
{ |
2933 |
14 Nov 14 |
olle |
129 |
cases.add(new Case(s)); |
2933 |
14 Nov 14 |
olle |
130 |
} |
2933 |
14 Nov 14 |
olle |
131 |
|
2933 |
14 Nov 14 |
olle |
132 |
return cases; |
2933 |
14 Nov 14 |
olle |
133 |
} |
2933 |
14 Nov 14 |
olle |
134 |
|
2933 |
14 Nov 14 |
olle |
135 |
/** |
2933 |
14 Nov 14 |
olle |
Get a list with all case numbers associated with a patient. This method look |
2933 |
14 Nov 14 |
olle |
for all sample of type {@link Subtype#SPECIMEN} for the given patient. |
2933 |
14 Nov 14 |
olle |
The case numbers are returned as strings sorted in numerical order. |
2933 |
14 Nov 14 |
olle |
@since 2.12 |
2933 |
14 Nov 14 |
olle |
140 |
*/ |
2933 |
14 Nov 14 |
olle |
141 |
public static List<String> findAllCaseNumbersByPatient(DbControl dc, Patient patient) |
2933 |
14 Nov 14 |
olle |
142 |
{ |
2933 |
14 Nov 14 |
olle |
143 |
Set<String> result = new TreeSet<String>(); |
2933 |
14 Nov 14 |
olle |
144 |
ItemQuery<Sample> query = Sample.getQuery(); |
2933 |
14 Nov 14 |
olle |
145 |
query.restrict(Restrictions.eq(Hql.property("parent"), Hql.entity(patient.getBioSource()))); |
2933 |
14 Nov 14 |
olle |
146 |
for (Sample blood : query.list(dc)) |
2933 |
14 Nov 14 |
olle |
147 |
{ |
4149 |
03 Oct 16 |
olle |
148 |
result.add(Meludi.fetchRootItemName(blood.getName(), dc.getSessionControl().getActiveProjectId())); |
2933 |
14 Nov 14 |
olle |
149 |
} |
2933 |
14 Nov 14 |
olle |
150 |
|
2933 |
14 Nov 14 |
olle |
151 |
query = Sample.getQuery(); |
2933 |
14 Nov 14 |
olle |
152 |
query.restrict(Restrictions.or(Subtype.SPECIMEN.restriction(dc, null))); |
2933 |
14 Nov 14 |
olle |
153 |
query.restrict(Restrictions.eq(Hql.property("parent.parent"), Hql.entity(patient.getBioSource()))); |
2933 |
14 Nov 14 |
olle |
154 |
for (Sample spec : query.list(dc)) |
2933 |
14 Nov 14 |
olle |
155 |
{ |
4149 |
03 Oct 16 |
olle |
156 |
result.add(Meludi.fetchRootItemName(spec.getName(), dc.getSessionControl().getActiveProjectId())); |
2933 |
14 Nov 14 |
olle |
157 |
} |
2933 |
14 Nov 14 |
olle |
158 |
return new ArrayList<String>(result); |
2933 |
14 Nov 14 |
olle |
159 |
} |
2933 |
14 Nov 14 |
olle |
160 |
|
4860 |
19 Jun 18 |
olle |
161 |
/** |
4860 |
19 Jun 18 |
olle |
Find a case by referral ID. If exactly one match is found all is |
4860 |
19 Jun 18 |
olle |
good and this is the case we are looking for. More than one match |
4860 |
19 Jun 18 |
olle |
is an error condition. No match indicates a case that has not yet |
4860 |
19 Jun 18 |
olle |
been registered (null is returned). |
4860 |
19 Jun 18 |
olle |
166 |
|
4860 |
19 Jun 18 |
olle |
@param dc DbControl The DbControl to use. |
4860 |
19 Jun 18 |
olle |
@param referralId String The referral ID. |
4860 |
19 Jun 18 |
olle |
@return Case The Case item found for the referral ID. |
4860 |
19 Jun 18 |
olle |
170 |
*/ |
4860 |
19 Jun 18 |
olle |
171 |
public static Case findByReferralId(DbControl dc, String referralId) |
4860 |
19 Jun 18 |
olle |
172 |
{ |
4860 |
19 Jun 18 |
olle |
173 |
Case theCase = null; |
4860 |
19 Jun 18 |
olle |
174 |
boolean merged = false; |
4860 |
19 Jun 18 |
olle |
175 |
|
4860 |
19 Jun 18 |
olle |
// Look for a case with the given referral ID as annotation. |
4860 |
19 Jun 18 |
olle |
177 |
AnnotationType refIdType = Annotationtype.REFERRAL_ID.load(dc); |
4860 |
19 Jun 18 |
olle |
178 |
ItemQuery<Sample> caseQuery = Sample.getQuery(); |
4860 |
19 Jun 18 |
olle |
179 |
Subtype.CASE.addFilter(dc, caseQuery); |
4860 |
19 Jun 18 |
olle |
180 |
caseQuery.restrict(new AnnotationSimpleRestriction(null, refIdType, Operator.EQ, referralId, true, false)); |
4860 |
19 Jun 18 |
olle |
181 |
caseQuery.setIncludes(Meludi.INCLUDE_IN_CURRENT_PROJECT); |
4860 |
19 Jun 18 |
olle |
182 |
List<Sample> cases = caseQuery.list(dc); |
4860 |
19 Jun 18 |
olle |
183 |
|
4860 |
19 Jun 18 |
olle |
184 |
if (cases.size() > 1) |
4860 |
19 Jun 18 |
olle |
185 |
{ |
4860 |
19 Jun 18 |
olle |
186 |
throw new InvalidDataException( |
4860 |
19 Jun 18 |
olle |
187 |
"More than one case with referral ID (" + referralId + ") was found. " + |
4860 |
19 Jun 18 |
olle |
188 |
"This wizard can't be used until that is corrected."); |
4860 |
19 Jun 18 |
olle |
189 |
} |
4860 |
19 Jun 18 |
olle |
190 |
if (cases.size() == 1) |
4860 |
19 Jun 18 |
olle |
191 |
{ |
4860 |
19 Jun 18 |
olle |
192 |
theCase = new Case(cases.get(0), cases.get(0).getName(), merged); |
4860 |
19 Jun 18 |
olle |
193 |
theCase.setAnnotation("referralId", referralId); |
4860 |
19 Jun 18 |
olle |
194 |
} |
4860 |
19 Jun 18 |
olle |
195 |
return theCase; |
4860 |
19 Jun 18 |
olle |
196 |
} |
4860 |
19 Jun 18 |
olle |
197 |
|
2933 |
14 Nov 14 |
olle |
198 |
public static Case getById(DbControl dc, int caseId) |
2933 |
14 Nov 14 |
olle |
199 |
{ |
2933 |
14 Nov 14 |
olle |
200 |
Sample s = Sample.getById(dc, caseId); |
2933 |
14 Nov 14 |
olle |
201 |
return s == null ? null : new Case(s); |
2933 |
14 Nov 14 |
olle |
202 |
} |
2933 |
14 Nov 14 |
olle |
203 |
|
2933 |
14 Nov 14 |
olle |
204 |
private final String originalName; |
2933 |
14 Nov 14 |
olle |
205 |
private final boolean merged; |
2933 |
14 Nov 14 |
olle |
206 |
|
2933 |
14 Nov 14 |
olle |
207 |
private Case(Sample sample) |
2933 |
14 Nov 14 |
olle |
208 |
{ |
2933 |
14 Nov 14 |
olle |
209 |
this(sample, sample.getName(), false); |
2933 |
14 Nov 14 |
olle |
210 |
} |
2933 |
14 Nov 14 |
olle |
211 |
|
2933 |
14 Nov 14 |
olle |
212 |
private Case(Sample sample, String originalName, boolean merged) |
2933 |
14 Nov 14 |
olle |
213 |
{ |
2933 |
14 Nov 14 |
olle |
214 |
super(sample); |
2933 |
14 Nov 14 |
olle |
215 |
this.originalName = originalName; |
2933 |
14 Nov 14 |
olle |
216 |
this.merged = merged; |
2933 |
14 Nov 14 |
olle |
217 |
} |
2933 |
14 Nov 14 |
olle |
218 |
|
2933 |
14 Nov 14 |
olle |
219 |
/** |
2933 |
14 Nov 14 |
olle |
Get the real sample that represents this case in BASE. |
2933 |
14 Nov 14 |
olle |
221 |
*/ |
2933 |
14 Nov 14 |
olle |
222 |
public Sample getSample() |
2933 |
14 Nov 14 |
olle |
223 |
{ |
2933 |
14 Nov 14 |
olle |
224 |
return getItem(); |
2933 |
14 Nov 14 |
olle |
225 |
} |
2933 |
14 Nov 14 |
olle |
226 |
|
2933 |
14 Nov 14 |
olle |
227 |
/** |
2933 |
14 Nov 14 |
olle |
If this case we were originally looking for is a case that was |
2933 |
14 Nov 14 |
olle |
merged with this case. The original case name is found in {@link #getOriginalName()}. |
2933 |
14 Nov 14 |
olle |
230 |
*/ |
2933 |
14 Nov 14 |
olle |
231 |
public boolean isMerged() |
2933 |
14 Nov 14 |
olle |
232 |
{ |
2933 |
14 Nov 14 |
olle |
233 |
return merged; |
2933 |
14 Nov 14 |
olle |
234 |
} |
2933 |
14 Nov 14 |
olle |
235 |
|
2933 |
14 Nov 14 |
olle |
236 |
/** |
2933 |
14 Nov 14 |
olle |
When searching for a case, it may have been merged to another case. It is always |
2933 |
14 Nov 14 |
olle |
the primary case that is returned by {@link #getSample()}. This method returns |
2933 |
14 Nov 14 |
olle |
the name of the case that was merged (or the primary case if no merge have been done). |
2933 |
14 Nov 14 |
olle |
240 |
*/ |
2933 |
14 Nov 14 |
olle |
241 |
public String getOriginalName() |
2933 |
14 Nov 14 |
olle |
242 |
{ |
2933 |
14 Nov 14 |
olle |
243 |
return originalName; |
2933 |
14 Nov 14 |
olle |
244 |
} |
2933 |
14 Nov 14 |
olle |
245 |
|
2933 |
14 Nov 14 |
olle |
246 |
@SuppressWarnings("unchecked") |
2933 |
14 Nov 14 |
olle |
247 |
@Override |
2933 |
14 Nov 14 |
olle |
248 |
protected void initJSON(JSONObject json) |
2933 |
14 Nov 14 |
olle |
249 |
{ |
2933 |
14 Nov 14 |
olle |
250 |
json.put("merged", merged); |
2933 |
14 Nov 14 |
olle |
251 |
json.put("originalName", originalName); |
2933 |
14 Nov 14 |
olle |
252 |
} |
2933 |
14 Nov 14 |
olle |
253 |
|
4773 |
20 Apr 18 |
olle |
254 |
/** |
4773 |
20 Apr 18 |
olle |
Verify that the patient has given their permission to participate in the |
4773 |
20 Apr 18 |
olle |
study. Due to the order that things get registered, a non-existing "Consent" |
4773 |
20 Apr 18 |
olle |
annotation is accepted. If the annotation exists the answer must be "Yes", |
4773 |
20 Apr 18 |
olle |
or an exception is thrown. |
4773 |
20 Apr 18 |
olle |
259 |
*/ |
4773 |
20 Apr 18 |
olle |
260 |
public void verifyConsent(DbControl dc, Annotationtype consentType) |
4773 |
20 Apr 18 |
olle |
261 |
{ |
4773 |
20 Apr 18 |
olle |
262 |
if (consentType == null) |
4773 |
20 Apr 18 |
olle |
263 |
{ |
4773 |
20 Apr 18 |
olle |
264 |
consentType = Annotationtype.CONSENT; |
4773 |
20 Apr 18 |
olle |
265 |
} |
4773 |
20 Apr 18 |
olle |
266 |
String consent = (String)consentType.getAnnotationValue(dc, getItem()); |
4773 |
20 Apr 18 |
olle |
267 |
if (consent != null && !consent.equals("Yes")) |
4773 |
20 Apr 18 |
olle |
268 |
{ |
4773 |
20 Apr 18 |
olle |
269 |
throw new PermissionDeniedException("The case (" + getOriginalName() + |
4773 |
20 Apr 18 |
olle |
270 |
") has not agreed to participate in the study."); |
4773 |
20 Apr 18 |
olle |
271 |
} |
4773 |
20 Apr 18 |
olle |
272 |
|
4773 |
20 Apr 18 |
olle |
273 |
} |
4773 |
20 Apr 18 |
olle |
274 |
|
2933 |
14 Nov 14 |
olle |
275 |
} |