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

Code
Comments
Other
Rev Date Author Line
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 28   Class for loading information that is related to cases.
2933 14 Nov 14 olle 29   
2933 14 Nov 14 olle 30   @author nicklas
2933 14 Nov 14 olle 31   @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 38     Find a case by name. This method will first check for an exact match
2933 14 Nov 14 olle 39     on the name of a sample. If exactly one match is found this is the
2933 14 Nov 14 olle 40     (primary) case. More than one match is an error condition. If no
2933 14 Nov 14 olle 41     match is found, we look harder by trying to find a child sample with 
2933 14 Nov 14 olle 42     'Specimen' or 'NoSpecimen' that have names matching the case we are looking 
2933 14 Nov 14 olle 43     for (eg. xxx.1, xxx.2, etc.). If exactly one match is found this is 
2933 14 Nov 14 olle 44     the (merged) case. More than one match is an error condition. No match 
2933 14 Nov 14 olle 45     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 52     // 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 55     // 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 61     // ...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 71       // Not found... see if we can find a different case 
2933 14 Nov 14 olle 72       // 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 80       // 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 89         // ...something is very incorrectly registered... 
2933 14 Nov 14 olle 90         // 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 108     Find all cases linked with a patient. A patient can have 0, 1 or 2
2933 14 Nov 14 olle 109     cases. If more than two are found an exception is thrown.
2933 14 Nov 14 olle 110     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 136     Get a list with all case numbers associated with a patient. This method look
2933 14 Nov 14 olle 137     for all sample of type {@link Subtype#SPECIMEN} for the given patient.
2933 14 Nov 14 olle 138     The case numbers are returned as strings sorted in numerical order.
2933 14 Nov 14 olle 139     @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 162     Find a case by referral ID. If exactly one match is found all is
4860 19 Jun 18 olle 163     good and this is the case we are looking for. More than one match
4860 19 Jun 18 olle 164     is an error condition. No match indicates a case that has not yet
4860 19 Jun 18 olle 165     been registered (null is returned).
4860 19 Jun 18 olle 166   
4860 19 Jun 18 olle 167     @param dc DbControl The DbControl to use.
4860 19 Jun 18 olle 168     @param referralId String The referral ID.
4860 19 Jun 18 olle 169     @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 176     // 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 220     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 228     If this case we were originally looking for is a case that was
2933 14 Nov 14 olle 229     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 237     When searching for a case, it may have been merged to another case. It is always
2933 14 Nov 14 olle 238     the primary case that is returned by {@link #getSample()}. This method returns
2933 14 Nov 14 olle 239     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 255     Verify that the patient has given their permission to participate in the
4773 20 Apr 18 olle 256     study. Due to the order that things get registered, a non-existing "Consent"
4773 20 Apr 18 olle 257     annotation is accepted. If the annotation exists the answer must be "Yes",
4773 20 Apr 18 olle 258     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 }