extensions/net.sf.basedb.reggie/trunk/src/net/sf/basedb/reggie/dao/Patient.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
3765 23 Feb 16 nicklas 3 import java.util.ArrayList;
3765 23 Feb 16 nicklas 4 import java.util.Collection;
1326 29 Mar 11 nicklas 5 import java.util.List;
2613 29 Aug 14 nicklas 6 import java.util.concurrent.locks.ReentrantLock;
1326 29 Mar 11 nicklas 7
7024 07 Feb 23 nicklas 8 import org.apache.commons.lang3.time.FastDateFormat;
7024 07 Feb 23 nicklas 9
4482 05 May 17 nicklas 10 import net.sf.basedb.core.AnnotationRestriction;
3247 14 Apr 15 nicklas 11 import net.sf.basedb.core.AnnotationSimpleRestriction;
1326 29 Mar 11 nicklas 12 import net.sf.basedb.core.AnnotationType;
1326 29 Mar 11 nicklas 13 import net.sf.basedb.core.BioSource;
1326 29 Mar 11 nicklas 14 import net.sf.basedb.core.DbControl;
1504 18 Jan 12 nicklas 15 import net.sf.basedb.core.Include;
1326 29 Mar 11 nicklas 16 import net.sf.basedb.core.InvalidDataException;
1326 29 Mar 11 nicklas 17 import net.sf.basedb.core.Item;
1326 29 Mar 11 nicklas 18 import net.sf.basedb.core.ItemQuery;
3247 14 Apr 15 nicklas 19 import net.sf.basedb.core.Operator;
1463 14 Nov 11 martin 20 import net.sf.basedb.core.Sample;
2613 29 Aug 14 nicklas 21 import net.sf.basedb.core.User;
1326 29 Mar 11 nicklas 22 import net.sf.basedb.core.query.Annotations;
1504 18 Jan 12 nicklas 23 import net.sf.basedb.core.query.Expressions;
1504 18 Jan 12 nicklas 24 import net.sf.basedb.core.query.Hql;
1504 18 Jan 12 nicklas 25 import net.sf.basedb.core.query.Orders;
1504 18 Jan 12 nicklas 26 import net.sf.basedb.core.query.Restrictions;
2613 29 Aug 14 nicklas 27 import net.sf.basedb.reggie.LockUtil;
2613 29 Aug 14 nicklas 28 import net.sf.basedb.reggie.Reggie;
1333 05 Apr 11 nicklas 29 import net.sf.basedb.reggie.converter.DateToStringConverter;
1326 29 Mar 11 nicklas 30
1326 29 Mar 11 nicklas 31 /**
1326 29 Mar 11 nicklas 32   Class for loading information related to a patient.
1326 29 Mar 11 nicklas 33   
1326 29 Mar 11 nicklas 34   @author nicklas
1326 29 Mar 11 nicklas 35   @since 1.2
1326 29 Mar 11 nicklas 36 */
1326 29 Mar 11 nicklas 37 public class Patient
1326 29 Mar 11 nicklas 38   extends ReggieItem<BioSource>
1326 29 Mar 11 nicklas 39 {
1326 29 Mar 11 nicklas 40
1326 29 Mar 11 nicklas 41   /**
1326 29 Mar 11 nicklas 42     Find a patient by personal number. If exactly one match is found all is
1326 29 Mar 11 nicklas 43     good and this is the patient we are looking for. More than one match
1326 29 Mar 11 nicklas 44     is an error condition. No match indicates a patient that has not yet
1326 29 Mar 11 nicklas 45     been registered (null is returned).
2839 20 Oct 14 olle 46     
2839 20 Oct 14 olle 47     @param dc DbControl The DbControl to use.
2839 20 Oct 14 olle 48     @param pnr String The personal number.
2839 20 Oct 14 olle 49     @return Patient The Patient item found for the personal number.
1326 29 Mar 11 nicklas 50   */
1326 29 Mar 11 nicklas 51   public static Patient findByPersonalNumber(DbControl dc, String pnr)
1326 29 Mar 11 nicklas 52   {
1326 29 Mar 11 nicklas 53     Patient patient = null;
1326 29 Mar 11 nicklas 54     
1610 23 Apr 12 nicklas 55     AnnotationType pnrType = Annotationtype.PERSONAL_NUMBER.load(dc);
1326 29 Mar 11 nicklas 56     ItemQuery<BioSource> patientQuery = BioSource.getQuery();
1503 18 Jan 12 nicklas 57     Subtype.PATIENT.addFilter(dc, patientQuery);
4482 05 May 17 nicklas 58     patientQuery.restrict(new AnnotationSimpleRestriction(null, pnrType, Operator.EQ, pnr, new AnnotationRestriction.Options()));
1326 29 Mar 11 nicklas 59     List<BioSource> patients = patientQuery.list(dc);
1326 29 Mar 11 nicklas 60     
1326 29 Mar 11 nicklas 61     if (patients.size() > 1)
1326 29 Mar 11 nicklas 62     {
1326 29 Mar 11 nicklas 63       throw new InvalidDataException(
1326 29 Mar 11 nicklas 64         "More than one patient with the personal number (" + pnr + ") was found. " +
1326 29 Mar 11 nicklas 65         "This wizard can't be used until that is corrected.");
1326 29 Mar 11 nicklas 66     }
1326 29 Mar 11 nicklas 67     if (patients.size() == 1)
1326 29 Mar 11 nicklas 68     {
1326 29 Mar 11 nicklas 69       patient = new Patient(patients.get(0));
1326 29 Mar 11 nicklas 70       patient.setAnnotation("personalNumber", pnr);
1326 29 Mar 11 nicklas 71     }
1326 29 Mar 11 nicklas 72     return patient;
1326 29 Mar 11 nicklas 73   }
1326 29 Mar 11 nicklas 74   
1326 29 Mar 11 nicklas 75   /**
4075 05 Sep 16 nicklas 76      Find a patient item with the given external ID.
4075 05 Sep 16 nicklas 77      @return A patient item, or null if not found
4075 05 Sep 16 nicklas 78      @since 4.7
4075 05 Sep 16 nicklas 79   */
4075 05 Sep 16 nicklas 80   public static Patient findByExternalId(DbControl dc, String externalId)
4075 05 Sep 16 nicklas 81   {
4075 05 Sep 16 nicklas 82     Patient item = null;
4075 05 Sep 16 nicklas 83     
4075 05 Sep 16 nicklas 84     ItemQuery<BioSource> query = BioSource.getQuery();
4075 05 Sep 16 nicklas 85     Subtype.PATIENT.addFilter(dc, query);
4075 05 Sep 16 nicklas 86     query.restrict(Restrictions.eq(Hql.property("externalId"), Expressions.string(externalId)));
4075 05 Sep 16 nicklas 87     query.order(Orders.desc(Hql.property("name")));
4075 05 Sep 16 nicklas 88     query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
4075 05 Sep 16 nicklas 89     
4075 05 Sep 16 nicklas 90     List<BioSource> items = query.list(dc);
4075 05 Sep 16 nicklas 91     if (items.size() > 1)
4075 05 Sep 16 nicklas 92     {
4075 05 Sep 16 nicklas 93       throw new InvalidDataException(
4075 05 Sep 16 nicklas 94           "More than one patient with the external id '" + externalId + "' was found. " +
4075 05 Sep 16 nicklas 95           "This wizard can't be used until that is corrected.");
4075 05 Sep 16 nicklas 96     }
4075 05 Sep 16 nicklas 97     if (items.size() == 1)
4075 05 Sep 16 nicklas 98     {
4075 05 Sep 16 nicklas 99       item = new Patient(items.get(0));
4075 05 Sep 16 nicklas 100     }
4075 05 Sep 16 nicklas 101     return item;
4075 05 Sep 16 nicklas 102   }
4075 05 Sep 16 nicklas 103
5278 05 Feb 19 nicklas 104   /**
5278 05 Feb 19 nicklas 105      Find a patient item with the given PATnnn name.
5278 05 Feb 19 nicklas 106      @return A patient item, or null if not found
5278 05 Feb 19 nicklas 107      @since 4.21
5278 05 Feb 19 nicklas 108   */
5278 05 Feb 19 nicklas 109   public static Patient findByName(DbControl dc, String name)
5278 05 Feb 19 nicklas 110   {
5278 05 Feb 19 nicklas 111     Patient item = null;
5278 05 Feb 19 nicklas 112     
5278 05 Feb 19 nicklas 113     ItemQuery<BioSource> query = BioSource.getQuery();
5278 05 Feb 19 nicklas 114     Subtype.PATIENT.addFilter(dc, query);
5278 05 Feb 19 nicklas 115     query.restrict(Restrictions.eq(Hql.property("name"), Expressions.string(name)));
5278 05 Feb 19 nicklas 116     query.setIncludes(Reggie.INCLUDE_IN_CURRENT_PROJECT);
5278 05 Feb 19 nicklas 117     
5278 05 Feb 19 nicklas 118     List<BioSource> items = query.list(dc);
5278 05 Feb 19 nicklas 119     if (items.size() > 1)
5278 05 Feb 19 nicklas 120     {
5278 05 Feb 19 nicklas 121       throw new InvalidDataException(
5278 05 Feb 19 nicklas 122           "More than one patient with name '" + name + "' was found. " +
5278 05 Feb 19 nicklas 123           "This wizard can't be used until that is corrected.");
5278 05 Feb 19 nicklas 124     }
5278 05 Feb 19 nicklas 125     if (items.size() == 1)
5278 05 Feb 19 nicklas 126     {
5278 05 Feb 19 nicklas 127       item = new Patient(items.get(0));
5278 05 Feb 19 nicklas 128     }
5278 05 Feb 19 nicklas 129     return item;
5278 05 Feb 19 nicklas 130   }
4075 05 Sep 16 nicklas 131   
4075 05 Sep 16 nicklas 132   /**
2839 20 Oct 14 olle 133     Find a patient by personal number. If exactly one match is found all is
2839 20 Oct 14 olle 134     good and this is the patient we are looking for. More than one match
2839 20 Oct 14 olle 135     is an error condition. No match indicates a patient that has not yet
2839 20 Oct 14 olle 136     been registered (null is returned).
2839 20 Oct 14 olle 137     
2839 20 Oct 14 olle 138     @param dc DbControl The DbControl to use.
2839 20 Oct 14 olle 139     @param pnr String The personal number.
2839 20 Oct 14 olle 140     @param restrictToPatientBioSources boolean Flag indicating if search should be restricted to Patient BioSources.
2839 20 Oct 14 olle 141     @return Patient The Patient item found for the personal number.
2839 20 Oct 14 olle 142   */
2839 20 Oct 14 olle 143   public static Patient findByPersonalNumber(DbControl dc, String pnr, boolean restrictToPatientBioSources)
2839 20 Oct 14 olle 144   {
2839 20 Oct 14 olle 145     Patient patient = null;
2839 20 Oct 14 olle 146   
2839 20 Oct 14 olle 147     AnnotationType pnrType = Annotationtype.PERSONAL_NUMBER.load(dc);
2839 20 Oct 14 olle 148     ItemQuery<BioSource> patientQuery = BioSource.getQuery();
2839 20 Oct 14 olle 149     if (restrictToPatientBioSources)
2839 20 Oct 14 olle 150     {
2839 20 Oct 14 olle 151       Subtype.PATIENT.addFilter(dc, patientQuery);
2839 20 Oct 14 olle 152     }
4482 05 May 17 nicklas 153     patientQuery.restrict(new AnnotationSimpleRestriction(null, pnrType, Operator.EQ, pnr, new AnnotationRestriction.Options()));
2839 20 Oct 14 olle 154     List<BioSource> patients = patientQuery.list(dc);
2839 20 Oct 14 olle 155   
2839 20 Oct 14 olle 156     if (patients.size() > 1)
2839 20 Oct 14 olle 157     {
2839 20 Oct 14 olle 158       throw new InvalidDataException(
2839 20 Oct 14 olle 159         "More than one patient with the personal number (" + pnr + ") was found. " +
2839 20 Oct 14 olle 160         "This wizard can't be used until that is corrected.");
2839 20 Oct 14 olle 161     }
2839 20 Oct 14 olle 162     if (patients.size() == 1)
2839 20 Oct 14 olle 163     {
2839 20 Oct 14 olle 164       patient = new Patient(patients.get(0));
2839 20 Oct 14 olle 165       patient.setAnnotation("personalNumber", pnr);
2839 20 Oct 14 olle 166     }
2839 20 Oct 14 olle 167     return patient;
2839 20 Oct 14 olle 168   }
2839 20 Oct 14 olle 169
2839 20 Oct 14 olle 170   /**
1326 29 Mar 11 nicklas 171     Find the patient the case is linked with. Null is returned if
1326 29 Mar 11 nicklas 172     not found.
1326 29 Mar 11 nicklas 173   */
1326 29 Mar 11 nicklas 174   public static Patient findByCase(DbControl dc, Case theCase)
1326 29 Mar 11 nicklas 175   {
1326 29 Mar 11 nicklas 176     Patient patient = null;
1463 14 Nov 11 martin 177     Sample s = theCase.getSample();
1463 14 Nov 11 martin 178     BioSource b = s.getParentType() == Item.BIOSOURCE ? (BioSource)s.getParent() : null;
1326 29 Mar 11 nicklas 179     if (b != null)
1326 29 Mar 11 nicklas 180     {
1326 29 Mar 11 nicklas 181       patient = new Patient(b);
1326 29 Mar 11 nicklas 182     }
1326 29 Mar 11 nicklas 183     return patient;
1326 29 Mar 11 nicklas 184   }
1326 29 Mar 11 nicklas 185   
1518 23 Jan 12 nicklas 186   /**
1518 23 Jan 12 nicklas 187     Find the patient the blood case is linked with. Null is returned if
1518 23 Jan 12 nicklas 188     not found.
1518 23 Jan 12 nicklas 189     @since 2.2
1518 23 Jan 12 nicklas 190   */
1518 23 Jan 12 nicklas 191   public static Patient findByBlood(DbControl dc, Blood blood)
1518 23 Jan 12 nicklas 192   {
1518 23 Jan 12 nicklas 193     Patient patient = null;
1518 23 Jan 12 nicklas 194     Sample s = blood.getSample();
1518 23 Jan 12 nicklas 195     BioSource b = s.getParentType() == Item.BIOSOURCE ? (BioSource)s.getParent() : null;
1518 23 Jan 12 nicklas 196     if (b != null)
1518 23 Jan 12 nicklas 197     {
1518 23 Jan 12 nicklas 198       patient = new Patient(b);
1518 23 Jan 12 nicklas 199     }
1518 23 Jan 12 nicklas 200     return patient;
1518 23 Jan 12 nicklas 201   }
3766 23 Feb 16 nicklas 202   
3766 23 Feb 16 nicklas 203   public static Patient getById(DbControl dc, int patientId)
3766 23 Feb 16 nicklas 204   {
3766 23 Feb 16 nicklas 205     BioSource bs = BioSource.getById(dc, patientId);
3766 23 Feb 16 nicklas 206     return bs == null ? null : new Patient(bs);
3766 23 Feb 16 nicklas 207   }
1518 23 Jan 12 nicklas 208
3765 23 Feb 16 nicklas 209   public static List<Patient> toPatient(Collection<BioSource> biosources)
3765 23 Feb 16 nicklas 210   {
3765 23 Feb 16 nicklas 211     List<Patient> patients = new ArrayList<Patient>(biosources.size());
3765 23 Feb 16 nicklas 212     for (BioSource bs : biosources)
3765 23 Feb 16 nicklas 213     {
3765 23 Feb 16 nicklas 214       patients.add(new Patient(bs));
3765 23 Feb 16 nicklas 215     }
3765 23 Feb 16 nicklas 216     return patients;
3765 23 Feb 16 nicklas 217   }
3765 23 Feb 16 nicklas 218   
7269 26 Jun 23 nicklas 219   public static Patient get(BioSource pat)
7269 26 Jun 23 nicklas 220   {
7269 26 Jun 23 nicklas 221     return pat == null ? null : new Patient(pat);
7269 26 Jun 23 nicklas 222   }
7269 26 Jun 23 nicklas 223
1504 18 Jan 12 nicklas 224   /**
1504 18 Jan 12 nicklas 225     Generate the next auto-generated patient name. This method will search all patients
1504 18 Jan 12 nicklas 226     starting with the given prefix and find the one with the highest numeric suffix. 
1504 18 Jan 12 nicklas 227     The returned name is the found patient + 1.
4894 09 Jul 18 nicklas 228     @since 4.19
4894 09 Jul 18 nicklas 229   */
4894 09 Jul 18 nicklas 230   public static String getNextName(DbControl dc, Subtype subtype, boolean releaseIfTransactionFails)
4894 09 Jul 18 nicklas 231   {
4894 09 Jul 18 nicklas 232     String prefix = subtype.getItemPrefix();
4894 09 Jul 18 nicklas 233     int numDigitsInName = 6;
4894 09 Jul 18 nicklas 234     
4894 09 Jul 18 nicklas 235     ItemQuery<BioSource> query = BioSource.getQuery();
4894 09 Jul 18 nicklas 236     subtype.addFilter(dc, query);
4894 09 Jul 18 nicklas 237     query.include(Include.ALL);
4894 09 Jul 18 nicklas 238     return ReggieItem.getNextItemName(dc, query, prefix, numDigitsInName, releaseIfTransactionFails);
4894 09 Jul 18 nicklas 239   }
4894 09 Jul 18 nicklas 240
4073 02 Sep 16 nicklas 241   /**
4076 05 Sep 16 nicklas 242     Generate the next auto-generated external ID. This method will search all patients
4073 02 Sep 16 nicklas 243     starting with the given prefix and find the one with the highest numeric suffix. 
4902 10 Jul 18 nicklas 244     The returned string is the found blood + 1.
4902 10 Jul 18 nicklas 245     @since 4.19
4073 02 Sep 16 nicklas 246   */
5088 13 Nov 18 nicklas 247   public static String getNextExternalId(DbControl dc, Subtype subtype)
4073 02 Sep 16 nicklas 248   {
4073 02 Sep 16 nicklas 249     ItemQuery<BioSource> patientQuery = BioSource.getQuery();
5088 13 Nov 18 nicklas 250     return ReggieItem.getNextExternalId(dc, patientQuery, subtype.getExternalIdPrefix());
4073 02 Sep 16 nicklas 251   }
4073 02 Sep 16 nicklas 252
4073 02 Sep 16 nicklas 253   
2613 29 Aug 14 nicklas 254   // Lock to prevent multiple patients with same name/personal from being registered
2613 29 Aug 14 nicklas 255   private static final ReentrantLock PATIENT_CHECK_LOCK = new ReentrantLock();
2613 29 Aug 14 nicklas 256   
2613 29 Aug 14 nicklas 257   /**
2613 29 Aug 14 nicklas 258     Check if a patient with the given personal number or name already exists
2613 29 Aug 14 nicklas 259     or not in the database. If the patient exists, an exception is thrown.
2613 29 Aug 14 nicklas 260     The first thread that calls this metod will block other treads from calling
2613 29 Aug 14 nicklas 261     it before the transaction is completed.
2613 29 Aug 14 nicklas 262     @since 2.16
2613 29 Aug 14 nicklas 263   */
2613 29 Aug 14 nicklas 264   public static void ensureNotExistingPatient(DbControl dc, String pnr, String name)
2613 29 Aug 14 nicklas 265   {
2613 29 Aug 14 nicklas 266     if (!LockUtil.acquireLockForTransaction(dc, PATIENT_CHECK_LOCK))
2613 29 Aug 14 nicklas 267     {
2613 29 Aug 14 nicklas 268       throw new RuntimeException("Could not aquire exclusive lock on patient database");
2613 29 Aug 14 nicklas 269     }
2613 29 Aug 14 nicklas 270     
2613 29 Aug 14 nicklas 271     ItemQuery<BioSource> query = BioSource.getQuery();
2613 29 Aug 14 nicklas 272     Subtype.PATIENT.addFilter(dc, query);
2613 29 Aug 14 nicklas 273     query.include(Reggie.INCLUDE_IN_CURRENT_PROJECT);
2613 29 Aug 14 nicklas 274     
2613 29 Aug 14 nicklas 275     query.join(Annotations.innerJoin(Annotationtype.PERSONAL_NUMBER.get(dc), "pn"));
2613 29 Aug 14 nicklas 276     query.restrict(Restrictions.or(
2613 29 Aug 14 nicklas 277       Restrictions.eq(Hql.property("name"), Expressions.string(name)),
2613 29 Aug 14 nicklas 278       Restrictions.eq(Hql.alias("pn"), Expressions.string(pnr))
2613 29 Aug 14 nicklas 279     ));
2613 29 Aug 14 nicklas 280     
2613 29 Aug 14 nicklas 281     List<BioSource> patients = query.list(dc);
2613 29 Aug 14 nicklas 282     if (patients.size() == 0) return;
2613 29 Aug 14 nicklas 283     
2613 29 Aug 14 nicklas 284     BioSource patient = patients.get(0);
2613 29 Aug 14 nicklas 285     User owner = patient.getOwner();
2613 29 Aug 14 nicklas 286     
2613 29 Aug 14 nicklas 287     throw new RuntimeException("Patient '" + (patient.getName().equals(name) ? name : pnr) + 
2613 29 Aug 14 nicklas 288         "' has already been registered by " + owner.getName());
2613 29 Aug 14 nicklas 289   }
2613 29 Aug 14 nicklas 290   
1326 29 Mar 11 nicklas 291   private Patient(BioSource bioSource)
1326 29 Mar 11 nicklas 292   {
1326 29 Mar 11 nicklas 293     super(bioSource);
1326 29 Mar 11 nicklas 294   }
1326 29 Mar 11 nicklas 295   
1326 29 Mar 11 nicklas 296   /**
1326 29 Mar 11 nicklas 297     Get the real biosource that represents this patient in BASE.
1326 29 Mar 11 nicklas 298   */
1326 29 Mar 11 nicklas 299   public BioSource getBioSource()
1326 29 Mar 11 nicklas 300   {
1326 29 Mar 11 nicklas 301     return getItem();
1326 29 Mar 11 nicklas 302   }
1326 29 Mar 11 nicklas 303   
1326 29 Mar 11 nicklas 304   /**
1326 29 Mar 11 nicklas 305     Load a default set of annotations for the patient.
1326 29 Mar 11 nicklas 306     <ul>
2082 17 Oct 13 nicklas 307     <li>{@link Annotationtype#PERSONAL_NUMBER}
2082 17 Oct 13 nicklas 308     <li>{@link Annotationtype#FAMILY_NAME}
2082 17 Oct 13 nicklas 309     <li>{@link Annotationtype#ALL_FIRST_NAMES}
2082 17 Oct 13 nicklas 310     <li>{@link Annotationtype#GENDER}
2082 17 Oct 13 nicklas 311     <li>{@link Annotationtype#DATE_OF_BIRTH}
1326 29 Mar 11 nicklas 312     </ul>
1326 29 Mar 11 nicklas 313   */
1326 29 Mar 11 nicklas 314   public void loadDefaultAnnotations(DbControl dc)
1326 29 Mar 11 nicklas 315   {
1610 23 Apr 12 nicklas 316     loadAnnotations(dc, "personalNumber", Annotationtype.PERSONAL_NUMBER, null);
1610 23 Apr 12 nicklas 317     loadAnnotations(dc, "familyName", Annotationtype.FAMILY_NAME, null);
1610 23 Apr 12 nicklas 318     loadAnnotations(dc, "allFirstNames", Annotationtype.ALL_FIRST_NAMES, null);
1610 23 Apr 12 nicklas 319     loadAnnotations(dc, "gender", Annotationtype.GENDER, null);
7024 07 Feb 23 nicklas 320     loadAnnotations(dc, "dateOfBirth", Annotationtype.DATE_OF_BIRTH, new DateToStringConverter(FastDateFormat.getInstance("yyyy-MM-dd")));
1326 29 Mar 11 nicklas 321   }
1326 29 Mar 11 nicklas 322
1326 29 Mar 11 nicklas 323 }