extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/dao/Case.java

Code
Comments
Other
Rev Date Author Line
1326 29 Mar 11 nicklas 1 package net.sf.basedb.reggie.dao;
1326 29 Mar 11 nicklas 2
1326 29 Mar 11 nicklas 3 import java.util.ArrayList;
5096 14 Nov 18 nicklas 4 import java.util.Collection;
1326 29 Mar 11 nicklas 5 import java.util.List;
1907 18 Mar 13 nicklas 6 import java.util.Set;
1907 18 Mar 13 nicklas 7 import java.util.TreeSet;
1326 29 Mar 11 nicklas 8
1326 29 Mar 11 nicklas 9 import org.json.simple.JSONObject;
1326 29 Mar 11 nicklas 10
1326 29 Mar 11 nicklas 11 import net.sf.basedb.core.DbControl;
1326 29 Mar 11 nicklas 12 import net.sf.basedb.core.Include;
1326 29 Mar 11 nicklas 13 import net.sf.basedb.core.InvalidDataException;
1326 29 Mar 11 nicklas 14 import net.sf.basedb.core.ItemQuery;
1341 11 Apr 11 nicklas 15 import net.sf.basedb.core.PermissionDeniedException;
1326 29 Mar 11 nicklas 16 import net.sf.basedb.core.Sample;
1326 29 Mar 11 nicklas 17 import net.sf.basedb.core.Type;
1326 29 Mar 11 nicklas 18 import net.sf.basedb.core.query.Expressions;
1326 29 Mar 11 nicklas 19 import net.sf.basedb.core.query.Hql;
1326 29 Mar 11 nicklas 20 import net.sf.basedb.core.query.Orders;
1326 29 Mar 11 nicklas 21 import net.sf.basedb.core.query.Restrictions;
4075 05 Sep 16 nicklas 22 import net.sf.basedb.reggie.Reggie;
1326 29 Mar 11 nicklas 23
1326 29 Mar 11 nicklas 24 /**
1326 29 Mar 11 nicklas 25   Class for loading information that is related to cases.
1326 29 Mar 11 nicklas 26   
1326 29 Mar 11 nicklas 27   @author nicklas
1326 29 Mar 11 nicklas 28   @since 1.2  
1326 29 Mar 11 nicklas 29 */
1326 29 Mar 11 nicklas 30 public class Case
1326 29 Mar 11 nicklas 31   extends ReggieItem<Sample>
1326 29 Mar 11 nicklas 32 {
1326 29 Mar 11 nicklas 33
1326 29 Mar 11 nicklas 34   /**
1326 29 Mar 11 nicklas 35     Find a case by name. This method will first check for an exact match
1326 29 Mar 11 nicklas 36     on the name of a sample. If exactly one match is found this is the
1326 29 Mar 11 nicklas 37     (primary) case. More than one match is an error condition. If no
1608 20 Apr 12 nicklas 38     match is found, we look harder by trying to find a child sample with 
6151 26 Feb 21 nicklas 39     'Specimen', 'NoSpecimen' or 'FutureSpecimen' that have names matching the case we are looking 
1326 29 Mar 11 nicklas 40     for (eg. xxx.1, xxx.2, etc.). If exactly one match is found this is 
1326 29 Mar 11 nicklas 41     the (merged) case. More than one match is an error condition. No match 
1326 29 Mar 11 nicklas 42     indicates a case that has not yet been registered (null is returned).
1326 29 Mar 11 nicklas 43   */
1326 29 Mar 11 nicklas 44   public static Case findByName(DbControl dc, String name)
1326 29 Mar 11 nicklas 45   {
1326 29 Mar 11 nicklas 46     Case theCase = null;
1326 29 Mar 11 nicklas 47     boolean merged = false;
1326 29 Mar 11 nicklas 48     
2840 20 Oct 14 nicklas 49     // Get rid of suffixes in the name (eg. C/D which is used for pre-neoadjuvant forms)
1622 25 Apr 12 nicklas 50     if (name.length() > 7) name = name.substring(0, 7);    
1622 25 Apr 12 nicklas 51     
1326 29 Mar 11 nicklas 52     // Look for a case with the given name (primary case)
1326 29 Mar 11 nicklas 53     ItemQuery<Sample> caseQuery = Sample.getQuery();
1503 18 Jan 12 nicklas 54     Subtype.CASE.addFilter(dc, caseQuery);
1326 29 Mar 11 nicklas 55     caseQuery.restrict(Restrictions.eq(Hql.property("name"), Expressions.parameter("name", name, Type.STRING)));
1326 29 Mar 11 nicklas 56     List<Sample> cases = caseQuery.list(dc);
1326 29 Mar 11 nicklas 57     
1326 29 Mar 11 nicklas 58     // ...if more than one is found, something is incorrectly registered... abort
1326 29 Mar 11 nicklas 59     if (cases.size() > 1)
1326 29 Mar 11 nicklas 60     {
1326 29 Mar 11 nicklas 61       throw new InvalidDataException(
1326 29 Mar 11 nicklas 62         "Found " + cases.size() + " cases with the same name (" + name + 
1326 29 Mar 11 nicklas 63         "). This wizard can't be used until that is corrected.");
1326 29 Mar 11 nicklas 64     }
1326 29 Mar 11 nicklas 65     
1326 29 Mar 11 nicklas 66     if (cases.size() == 0)
1326 29 Mar 11 nicklas 67     {
1326 29 Mar 11 nicklas 68       // Not found... see if we can find a different case 
6510 03 Dec 21 nicklas 69       // with 'Specimen'/'NoSpecimen' child items belonging to this case (merged case)
1326 29 Mar 11 nicklas 70       merged = true;
1326 29 Mar 11 nicklas 71       caseQuery = Sample.getQuery();
1503 18 Jan 12 nicklas 72       Subtype.CASE.addFilter(dc, caseQuery);
1463 14 Nov 11 martin 73       caseQuery.join(Hql.innerJoin("childCreationEvents", "cce"));
1463 14 Nov 11 martin 74       caseQuery.join(Hql.innerJoin("cce", "event", "evt"));
1492 02 Dec 11 nicklas 75       caseQuery.join(Hql.innerJoin("evt", "bioMaterial", "sp")); // 'sp' should now reference a specimen tube
1608 20 Apr 12 nicklas 76       
6510 03 Dec 21 nicklas 77       // Restrict to 'Specimen' or 'NoSpecimen' or 'FutureSpecimen' child items
6510 03 Dec 21 nicklas 78       caseQuery.restrict(Restrictions.or(Subtype.SPECIMEN.restriction(dc, "sp"), Subtype.NO_SPECIMEN.restriction(dc, "sp")));
1492 02 Dec 11 nicklas 79       caseQuery.restrict(Restrictions.like(Hql.property("sp", "name"), Expressions.parameter("name", name + ".%", Type.STRING)));
1326 29 Mar 11 nicklas 80       caseQuery.setDistinct(true);
1366 23 May 11 nicklas 81       
1326 29 Mar 11 nicklas 82       cases = caseQuery.list(dc);
1326 29 Mar 11 nicklas 83       
1326 29 Mar 11 nicklas 84       if (cases.size() > 1)
1326 29 Mar 11 nicklas 85       {
1326 29 Mar 11 nicklas 86         // ...something is very incorrectly registered... 
1326 29 Mar 11 nicklas 87         // we have specimen tubes xxx.1, xxx.2, etc. with different parents
1326 29 Mar 11 nicklas 88         throw new InvalidDataException(
1326 29 Mar 11 nicklas 89           "The specimen tubes for this case (" + name + ") are already linked with " + cases.size() +
1326 29 Mar 11 nicklas 90           " different cases. This wizard can't be used until that is corrected.");
1326 29 Mar 11 nicklas 91       }
1326 29 Mar 11 nicklas 92       
1326 29 Mar 11 nicklas 93     }
1326 29 Mar 11 nicklas 94
1326 29 Mar 11 nicklas 95     if (cases.size() == 1)
1326 29 Mar 11 nicklas 96     {
1326 29 Mar 11 nicklas 97       theCase = new Case(cases.get(0), name, merged);
1326 29 Mar 11 nicklas 98     }
1326 29 Mar 11 nicklas 99     
1326 29 Mar 11 nicklas 100     return theCase;
1326 29 Mar 11 nicklas 101
1326 29 Mar 11 nicklas 102   }
1326 29 Mar 11 nicklas 103   
1326 29 Mar 11 nicklas 104   /**
1326 29 Mar 11 nicklas 105     Find all cases linked with a patient. A patient can have 0, 1 or 2
2144 19 Nov 13 olle 106     cases. If more than two are found an exception is thrown.
2144 19 Nov 13 olle 107     The cases are sorted in ascending order after case name.
1326 29 Mar 11 nicklas 108   */
1326 29 Mar 11 nicklas 109   public static List<Case> findByPatient(DbControl dc, Patient patient)
1326 29 Mar 11 nicklas 110   {
1326 29 Mar 11 nicklas 111     ItemQuery<Sample> caseQuery = patient.getBioSource().getSamples();
1503 18 Jan 12 nicklas 112     Subtype.CASE.addFilter(dc, caseQuery);
1326 29 Mar 11 nicklas 113     caseQuery.include(Include.ALL);
1326 29 Mar 11 nicklas 114     caseQuery.order(Orders.asc(Hql.property("name")));
1326 29 Mar 11 nicklas 115     List<Sample> tmp = caseQuery.list(dc);
1326 29 Mar 11 nicklas 116     if (tmp.size() > 2)
1326 29 Mar 11 nicklas 117     {
1326 29 Mar 11 nicklas 118       throw new InvalidDataException("The patient (" + patient.getName() + 
1326 29 Mar 11 nicklas 119         ") has more than two cases associated with. " +
1326 29 Mar 11 nicklas 120         "This wizard can't be used until that is corrected.");
1326 29 Mar 11 nicklas 121     }
1326 29 Mar 11 nicklas 122     
1326 29 Mar 11 nicklas 123     List<Case> cases = new ArrayList<Case>(tmp.size());
1326 29 Mar 11 nicklas 124     for (Sample s : caseQuery.list(dc))
1326 29 Mar 11 nicklas 125     {
1326 29 Mar 11 nicklas 126       cases.add(new Case(s));
1326 29 Mar 11 nicklas 127     }
1326 29 Mar 11 nicklas 128     
1326 29 Mar 11 nicklas 129     return cases;
1326 29 Mar 11 nicklas 130   }
1326 29 Mar 11 nicklas 131   
1907 18 Mar 13 nicklas 132   /**
4075 05 Sep 16 nicklas 133      Find a case item with the given external ID.
4075 05 Sep 16 nicklas 134      @return A case item, or null if not found
4075 05 Sep 16 nicklas 135      @since 4.7
4075 05 Sep 16 nicklas 136   */
4075 05 Sep 16 nicklas 137   public static Case findByExternalId(DbControl dc, String externalId)
4075 05 Sep 16 nicklas 138   {
4075 05 Sep 16 nicklas 139     Case item = null;
4075 05 Sep 16 nicklas 140     
4075 05 Sep 16 nicklas 141     ItemQuery<Sample> query = Sample.getQuery();
4075 05 Sep 16 nicklas 142     Subtype.CASE.addFilter(dc, query);
4075 05 Sep 16 nicklas 143     query.restrict(Restrictions.eq(Hql.property("externalId"), Expressions.string(externalId)));
4075 05 Sep 16 nicklas 144     query.order(Orders.desc(Hql.property("name")));
4075 05 Sep 16 nicklas 145     query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4075 05 Sep 16 nicklas 146     
4075 05 Sep 16 nicklas 147     List<Sample> items = query.list(dc);
4075 05 Sep 16 nicklas 148     if (items.size() > 1)
4075 05 Sep 16 nicklas 149     {
4075 05 Sep 16 nicklas 150       throw new InvalidDataException(
4075 05 Sep 16 nicklas 151           "More than one case with the external id '" + externalId + "' was found. " +
4075 05 Sep 16 nicklas 152           "This wizard can't be used until that is corrected.");
4075 05 Sep 16 nicklas 153     }
4075 05 Sep 16 nicklas 154     if (items.size() == 1)
4075 05 Sep 16 nicklas 155     {
4075 05 Sep 16 nicklas 156       item = new Case(items.get(0));
4075 05 Sep 16 nicklas 157     }
4075 05 Sep 16 nicklas 158     return item;
4075 05 Sep 16 nicklas 159   }
4075 05 Sep 16 nicklas 160
4075 05 Sep 16 nicklas 161   
4075 05 Sep 16 nicklas 162   /**
1907 18 Mar 13 nicklas 163     Get a list with all case numbers associated with a patient. This method look
6510 03 Dec 21 nicklas 164     for all sample of type {@link Subtype#SPECIMEN}, {@link Subtype#NO_SPECIMEN} 
6510 03 Dec 21 nicklas 165     and {@link Subtype#BLOOD} for the given patient. 
6151 26 Feb 21 nicklas 166     The case numbers are returned as strings sorted in numerical order.
1907 18 Mar 13 nicklas 167     @since 2.12
1907 18 Mar 13 nicklas 168   */
2592 19 Aug 14 nicklas 169   public static List<String> findAllCaseNumbersByPatient(DbControl dc, Patient patient)
1907 18 Mar 13 nicklas 170   {
1907 18 Mar 13 nicklas 171     Set<String> result = new TreeSet<String>();
1907 18 Mar 13 nicklas 172     ItemQuery<Sample> query = Sample.getQuery();
1907 18 Mar 13 nicklas 173     Subtype.BLOOD.addFilter(dc, query);
1907 18 Mar 13 nicklas 174     query.restrict(Restrictions.eq(Hql.property("parent"), Hql.entity(patient.getBioSource())));
1907 18 Mar 13 nicklas 175     for (Sample blood : query.list(dc))
1907 18 Mar 13 nicklas 176     {
1907 18 Mar 13 nicklas 177       result.add(blood.getName().substring(0, 7));
1907 18 Mar 13 nicklas 178     }
1907 18 Mar 13 nicklas 179     
1907 18 Mar 13 nicklas 180     query = Sample.getQuery();
6510 03 Dec 21 nicklas 181     query.restrict(Restrictions.or(Subtype.SPECIMEN.restriction(dc, null), Subtype.NO_SPECIMEN.restriction(dc, null)));
1907 18 Mar 13 nicklas 182     query.restrict(Restrictions.eq(Hql.property("parent.parent"), Hql.entity(patient.getBioSource())));
1907 18 Mar 13 nicklas 183     for (Sample spec : query.list(dc))
1907 18 Mar 13 nicklas 184     {
1907 18 Mar 13 nicklas 185       result.add(spec.getName().substring(0, 7));
1907 18 Mar 13 nicklas 186     }
2592 19 Aug 14 nicklas 187     return new ArrayList<String>(result);
1907 18 Mar 13 nicklas 188   }
1907 18 Mar 13 nicklas 189   
4069 02 Sep 16 nicklas 190   /**
4069 02 Sep 16 nicklas 191     Generate the next auto-generated external ID. This method will search all cases
4069 02 Sep 16 nicklas 192     starting with the given prefix and find the one with the highest numeric suffix. 
4902 10 Jul 18 nicklas 193     The returned string is the found case + 1.
4902 10 Jul 18 nicklas 194     @since 4.19
4069 02 Sep 16 nicklas 195   */
4902 10 Jul 18 nicklas 196   public static String getNextExternalId(DbControl dc)
4069 02 Sep 16 nicklas 197   {
4069 02 Sep 16 nicklas 198     ItemQuery<Sample> caseQuery = Sample.getQuery();
5088 13 Nov 18 nicklas 199     return ReggieItem.getNextExternalId(dc, caseQuery, Subtype.CASE.getExternalIdPrefix());
4069 02 Sep 16 nicklas 200   }
4069 02 Sep 16 nicklas 201   
1335 05 Apr 11 nicklas 202   public static Case getById(DbControl dc, int caseId)
1335 05 Apr 11 nicklas 203   {
1335 05 Apr 11 nicklas 204     Sample s = Sample.getById(dc, caseId);
1335 05 Apr 11 nicklas 205     return s == null ? null : new Case(s);
1335 05 Apr 11 nicklas 206   }
1326 29 Mar 11 nicklas 207   
5096 14 Nov 18 nicklas 208   public static List<Case> toList(Collection<Sample> samples)
5096 14 Nov 18 nicklas 209   {
5096 14 Nov 18 nicklas 210     List<Case> cases = new ArrayList<Case>(samples.size());
5096 14 Nov 18 nicklas 211     for (Sample s : samples)
5096 14 Nov 18 nicklas 212     {
5096 14 Nov 18 nicklas 213       cases.add(new Case(s));
5096 14 Nov 18 nicklas 214     }
5096 14 Nov 18 nicklas 215     return cases;
5096 14 Nov 18 nicklas 216   }
5096 14 Nov 18 nicklas 217
3940 16 May 16 nicklas 218   /**
3940 16 May 16 nicklas 219     @since 4.5
3940 16 May 16 nicklas 220   */
3940 16 May 16 nicklas 221   public static Case get(Sample sample)
3940 16 May 16 nicklas 222   {
7143 05 May 23 nicklas 223     return sample == null ? null : new Case(sample);
3940 16 May 16 nicklas 224   }
3940 16 May 16 nicklas 225
1326 29 Mar 11 nicklas 226   private final String originalName;
1326 29 Mar 11 nicklas 227   private final boolean merged;
1326 29 Mar 11 nicklas 228   
1326 29 Mar 11 nicklas 229   private Case(Sample sample)
1326 29 Mar 11 nicklas 230   {
1326 29 Mar 11 nicklas 231     this(sample, sample.getName(), false);
1326 29 Mar 11 nicklas 232   }
1326 29 Mar 11 nicklas 233   
1326 29 Mar 11 nicklas 234   private Case(Sample sample, String originalName, boolean merged)
1326 29 Mar 11 nicklas 235   {
1326 29 Mar 11 nicklas 236     super(sample);
1326 29 Mar 11 nicklas 237     this.originalName = originalName;
1326 29 Mar 11 nicklas 238     this.merged = merged;
1326 29 Mar 11 nicklas 239   }
1326 29 Mar 11 nicklas 240   
1326 29 Mar 11 nicklas 241   /**
1326 29 Mar 11 nicklas 242     Get the real sample that represents this case in BASE.
1326 29 Mar 11 nicklas 243   */
1326 29 Mar 11 nicklas 244   public Sample getSample()
1326 29 Mar 11 nicklas 245   {
1326 29 Mar 11 nicklas 246     return getItem();
1326 29 Mar 11 nicklas 247   }
1326 29 Mar 11 nicklas 248   
1326 29 Mar 11 nicklas 249   /**
1326 29 Mar 11 nicklas 250     If this case we were originally looking for is a case that was
1326 29 Mar 11 nicklas 251     merged with this case. The original case name is found in {@link #getOriginalName()}.
1326 29 Mar 11 nicklas 252   */
1326 29 Mar 11 nicklas 253   public boolean isMerged()
1326 29 Mar 11 nicklas 254   {
1326 29 Mar 11 nicklas 255     return merged;
1326 29 Mar 11 nicklas 256   }
1326 29 Mar 11 nicklas 257   
1326 29 Mar 11 nicklas 258   /**
1326 29 Mar 11 nicklas 259     When searching for a case, it may have been merged to another case. It is always
1326 29 Mar 11 nicklas 260     the primary case that is returned by {@link #getSample()}. This method returns
1326 29 Mar 11 nicklas 261     the name of the case that was merged (or the primary case if no merge have been done).
1326 29 Mar 11 nicklas 262   */
1326 29 Mar 11 nicklas 263   public String getOriginalName()
1326 29 Mar 11 nicklas 264   {
1326 29 Mar 11 nicklas 265     return originalName;
1326 29 Mar 11 nicklas 266   }
1326 29 Mar 11 nicklas 267   
1326 29 Mar 11 nicklas 268   @Override
1326 29 Mar 11 nicklas 269   protected void initJSON(JSONObject json)
1326 29 Mar 11 nicklas 270   {
1326 29 Mar 11 nicklas 271     json.put("merged", merged);
1326 29 Mar 11 nicklas 272     json.put("originalName", originalName);
1326 29 Mar 11 nicklas 273   }
1326 29 Mar 11 nicklas 274   
1341 11 Apr 11 nicklas 275   /**
1341 11 Apr 11 nicklas 276     Verify that the patient has given their permission to participate in the
1341 11 Apr 11 nicklas 277     study. Due to the order that things get registered, a non-existing "Consent"
1341 11 Apr 11 nicklas 278     annotation is accepted. If the annotation exists the answer must be "Yes",
1341 11 Apr 11 nicklas 279     or an exception is thrown.
1341 11 Apr 11 nicklas 280   */
1610 23 Apr 12 nicklas 281   public void verifyConsent(DbControl dc, Annotationtype consentType)
1341 11 Apr 11 nicklas 282   {
1341 11 Apr 11 nicklas 283     if (consentType == null)
1341 11 Apr 11 nicklas 284     {
1610 23 Apr 12 nicklas 285       consentType = Annotationtype.CONSENT;
1341 11 Apr 11 nicklas 286     }
1610 23 Apr 12 nicklas 287     String consent = (String)consentType.getAnnotationValue(dc, getItem());
1341 11 Apr 11 nicklas 288     if (consent != null && !consent.equals("Yes"))
1341 11 Apr 11 nicklas 289     {
1341 11 Apr 11 nicklas 290       throw new PermissionDeniedException("The case (" + getOriginalName() + 
1341 11 Apr 11 nicklas 291         ") has not agreed to participate in the study.");
1341 11 Apr 11 nicklas 292     }
1341 11 Apr 11 nicklas 293
1341 11 Apr 11 nicklas 294   }
1326 29 Mar 11 nicklas 295   
5777 06 Dec 19 nicklas 296   /**
5777 06 Dec 19 nicklas 297     Find the next name to give a TMA specimen child item. This assumes that
5777 06 Dec 19 nicklas 298     all child items are using the naming convention. foo.tma, foo.tma2, and
5777 06 Dec 19 nicklas 299     so on. NOTE! The first child item have no number!
5777 06 Dec 19 nicklas 300     @return The next unused name
5777 06 Dec 19 nicklas 301     @since 4.25
5777 06 Dec 19 nicklas 302   */
5777 06 Dec 19 nicklas 303   public String getNextTMASpecimenName(DbControl dc)
5777 06 Dec 19 nicklas 304   {
5777 06 Dec 19 nicklas 305     Sample c = getItem();
6193 30 Mar 21 nicklas 306     ItemQuery<Sample> query = null;
6193 30 Mar 21 nicklas 307     if (c.isInDatabase())
6193 30 Mar 21 nicklas 308     {
6193 30 Mar 21 nicklas 309       query = c.getChildSamples();
6193 30 Mar 21 nicklas 310       Subtype.TMA_SPECIMEN.addFilter(dc, query);
6193 30 Mar 21 nicklas 311       query.setIncludes(Include.ALL);
6193 30 Mar 21 nicklas 312     }
5777 06 Dec 19 nicklas 313     return getNextChildItemName(dc, query, Subtype.TMA_SPECIMEN.getItemSuffix(), true);
5777 06 Dec 19 nicklas 314   }
5777 06 Dec 19 nicklas 315
1326 29 Mar 11 nicklas 316 }