Commit 323276f1 authored by R.W.Majeed's avatar R.W.Majeed

deprecated VisitStore.

new integrated implementation of PatientVisitCache to ensure visit/patient assignment is always in sync
parent 6f42960e
......@@ -61,4 +61,20 @@ public interface Extension<T>{
Class<?>[] getInstanceTypes();
Class<T> getSlotType();
/**
* Extract subtype information from the slot type.
* E.g. a visit store can provide info about the patient
* @param slotInstance slot instance type
* @param subtype subtype to retrieve
* @return subtype instance
*/
<U> U extractSubtype(T slotInstance, Class<U> subtype);
public static <U,T> U extractSupertype(T slotInstance, Class<U> supertype){
if( supertype.isInstance(slotInstance) ) {
return supertype.cast(slotInstance);
}else {
throw new IllegalArgumentException("Unsupported supertype "+supertype);
}
}
}
package de.sekmi.histream.ext;
import java.util.List;
public interface PatientVisitStore {
Patient findPatient(String patientId);
Visit findVisit(String visitId);
void merge(Patient patient, String additionalId, ExternalSourceType source);
/**
* Get alias ids for the given patient (e.g. resulting from a merge)
* @param patient patient instance
* @return alias ids
*/
String[] getPatientAliasIds(Patient patient);
/**
* Deletes the patient identified by given id. This method does not remove any other associated
* data e.g. like visits, observations.
* @param id patient id
*/
void purgePatient(String patientId);
void purgeVisit(String visitId);
List<? extends Visit> allVisits(Patient patient);
}
......@@ -9,6 +9,7 @@ import java.util.Map;
* @author R.W.Majeed
*
*/
@Deprecated
public class CachedPatientExtension extends SimplePatientExtension {
private Map<String, PatientImpl> cache;
......
......@@ -32,6 +32,7 @@ import de.sekmi.histream.ext.Patient;
* @author R.W.Majeed
*
*/
@Deprecated
public class SimplePatientExtension implements Extension<PatientImpl>{
private final static Class<?>[] TYPES = new Class[] {Patient.class, PatientImpl.class};
......@@ -65,4 +66,9 @@ public class SimplePatientExtension implements Extension<PatientImpl>{
return PatientImpl.class;
}
@Override
public <U> U extractSubtype(PatientImpl slotInstance, Class<U> subtype) {
return extractSubtype(slotInstance, subtype);
}
}
package de.sekmi.histream.impl;
/*
* #%L
* histream
* %%
* Copyright (C) 2013 - 2015 R.W.Majeed
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import de.sekmi.histream.Observation;
import de.sekmi.histream.Extension;
import de.sekmi.histream.ext.ExternalSourceType;
import de.sekmi.histream.ext.Patient;
import de.sekmi.histream.ext.Visit;
public class SimplePatientVisitExtension implements Extension<VisitPatientImpl>{
private final static Class<?>[] TYPES = new Class[] {Visit.class,VisitPatientImpl.class,Patient.class,PatientImpl.class};
@Override
public Class<?>[] getInstanceTypes() {return TYPES;}
@Override
public VisitPatientImpl createInstance(Object... args) {
if( args.length != 3
|| !(args[0] instanceof String)
|| !(args[1] instanceof String)
|| !(args[2] instanceof ExternalSourceType) )
{
throw new IllegalArgumentException("Need arguments Patient id, Visit id, ExternalSourceType");
}
ExternalSourceType source = (ExternalSourceType)args[2];
PatientImpl patient = new PatientImpl();
patient.setId((String)args[0]);
patient.setSourceId(source.getSourceId());
patient.setSourceTimestamp(source.getSourceTimestamp());
VisitPatientImpl visit = new VisitPatientImpl((String)args[1], patient, null);
visit.setSourceId(source.getSourceId());
visit.setSourceTimestamp(source.getSourceTimestamp());
return visit;
}
@Override
public VisitPatientImpl createInstance(Observation observation) {
VisitPatientImpl visit = createInstance(observation.getPatientId(), observation.getEncounterId(), observation.getSource());
//visit.setId();
//visit.setPatientId(observation.getPatientId());
//visit.setSourceId(observation.getSourceId());
//visit.setSourceTimestamp(observation.getSourceTimestamp());
return visit;
}
@Override
public Class<VisitPatientImpl> getSlotType() {
return VisitPatientImpl.class;
}
@Override
public <U> U extractSubtype(VisitPatientImpl slotInstance, Class<U> subtype) {
if( subtype.isAssignableFrom(PatientImpl.class) ){
return subtype.cast(slotInstance.getPatient());
}else if( subtype.isInstance(slotInstance) ) {
return subtype.cast(slotInstance);
}else {
throw new IllegalArgumentException("Unsupported subtype "+subtype);
}
}
}
......@@ -29,6 +29,7 @@ import de.sekmi.histream.ext.ExternalSourceType;
import de.sekmi.histream.ext.Patient;
import de.sekmi.histream.ext.Visit;
@Deprecated
public class SimpleVisitExtension implements Extension<VisitImpl>{
private final static Class<?>[] TYPES = new Class[] {Visit.class, VisitImpl.class};
......@@ -68,5 +69,9 @@ public class SimpleVisitExtension implements Extension<VisitImpl>{
public Class<VisitImpl> getSlotType() {
return VisitImpl.class;
}
@Override
public <U> U extractSubtype(VisitImpl slotInstance, Class<U> subtype) {
return extractSubtype(slotInstance, subtype);
}
}
......@@ -30,6 +30,7 @@ import de.sekmi.histream.ext.Patient;
import de.sekmi.histream.ext.StoredExtensionType;
import de.sekmi.histream.ext.Visit;
@Deprecated
public class VisitImpl extends StoredExtensionType implements Visit {
private DateTimeAccuracy startTime;
private DateTimeAccuracy endTime;
......
package de.sekmi.histream.impl;
import java.util.Objects;
/*
* #%L
* histream
* %%
* Copyright (C) 2013 - 2015 R.W.Majeed
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import de.sekmi.histream.DateTimeAccuracy;
import de.sekmi.histream.ext.StoredExtensionType;
import de.sekmi.histream.ext.Visit;
public class VisitPatientImpl extends StoredExtensionType implements Visit {
private PatientImpl patient;
private DateTimeAccuracy startTime;
private DateTimeAccuracy endTime;
private Status status;
private String locationId;
private String providerId;
/**
* Empty constructor protected, only
* available to overriding classes.
*/
protected VisitPatientImpl() {
}
public VisitPatientImpl(String id, PatientImpl patient, DateTimeAccuracy startTime){
setId(id);
setPatient(patient);
this.startTime = startTime;
}
public String getPatientId(){return patient.getId();}
public void setPatient(PatientImpl patient){
Objects.requireNonNull(patient);
// patient id should not be changed normally.
this.patient = patient;
markDirty(true);
}
public PatientImpl getPatient() {
return this.patient;
}
@Override
public DateTimeAccuracy getStartTime() {
return startTime;
}
@Override
public DateTimeAccuracy getEndTime() {
return endTime;
}
@Override
public Status getStatus() {
return this.status;
}
@Override
public void setStatus(Status status) {
checkAndUpdateDirty(this.status, status);
this.status = status;
}
@Override
public String getLocationId() {
return locationId;
}
@Override
public void setLocationId(String locationId){
checkAndUpdateDirty(this.locationId, locationId);
this.locationId = locationId;
}
@Override
public void setEndTime(DateTimeAccuracy endTime) {
checkAndUpdateDirty(this.endTime, endTime);
this.endTime = endTime;
}
@Override
public void setStartTime(DateTimeAccuracy startTime) {
checkAndUpdateDirty(this.startTime, startTime);
this.startTime = startTime;
}
@Override
public String getProviderId() {
return this.providerId;
}
@Override
public void setProviderId(String providerId) {
checkAndUpdateDirty(this.providerId, providerId);
this.providerId = providerId;
}
}
package de.sekmi.histream.i2b2;
import java.time.temporal.ChronoUnit;
import de.sekmi.histream.ext.Visit;
import de.sekmi.histream.impl.PatientImpl;
/*
* #%L
* histream
* %%
* Copyright (C) 2013 - 2015 R.W.Majeed
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import de.sekmi.histream.impl.VisitPatientImpl;
/**
* I2b2 visit. The active encounter_ide is returned by {@link #getId()}.
*
* @author Raphael
*
*/
public class I2b2PatientVisit extends VisitPatientImpl {
/**
* I2b2 internal encounter id (32bit integer)
*/
private int encounter_num;
private int patient_num;
/**
* String id aliases for the encounter
*/
String[] aliasIds;
/**
* Index in aliasIds for the primary alias
*/
int primaryAliasIndex;
int maxInstanceNum;
public I2b2PatientVisit(int encounter_num, int patient_num) {
super();
this.encounter_num = encounter_num;
this.patient_num = patient_num;
maxInstanceNum = 1;
// TODO set startDate, endDate
}
public int getNum(){return encounter_num;}
public int getPatientNum(){return patient_num;}
@Override
public void setPatient(PatientImpl patient) {
super.setPatient(patient);
if( patient instanceof I2b2Patient ) {
// also set the patient_num
int patient_num = ((I2b2Patient)patient).getNum();
this.patient_num = patient_num;
}else {
throw new IllegalArgumentException("Patient expected of instanceOf I2b2Patient");
}
}
@Override
public String toString(){
return "I2b2Visit(encounter_um="+encounter_num+")";
}
/**
* Get the i2b2 vital_status_cd for this visit.
* @return vital status code, see CRC_Design doc
*/
public String getActiveStatusCd(){
Visit visit = this;
char end_char=0, start_char=0;
if( visit.getEndTime() != null ){
switch( visit.getEndTime().getAccuracy() ){
case DAYS:
end_char = 0; // same meaning
end_char = 'Y';
break;
case MONTHS:
end_char = 'M';
break;
case YEARS:
end_char = 'X';
break;
case HOURS:
end_char = 'R';
break;
case MINUTES:
end_char = 'T';
break;
case SECONDS:
end_char = 'S';
break;
default:
}
}else{
// null end date
// U: unknown, O: ongoing
// default to unknown
end_char = 'U';
}
// start date
if( visit.getStartTime() != null ){
switch( visit.getStartTime().getAccuracy() ){
case DAYS:
start_char = 0; // same meaning
start_char = 'D';
break;
case MONTHS:
start_char = 'B';
break;
case YEARS:
start_char = 'F';
break;
case HOURS:
start_char = 'H';
break;
case MINUTES:
start_char = 'I';
break;
case SECONDS:
start_char = 'C';
break;
default:
}
}else{
// null start date
// L: unknown, A: active
// default to unknown
start_char = 'L';
}
if( end_char != 0 && start_char != 0 )
return new String(new char[]{end_char,start_char});
else if( end_char != 0 )
return new String(new char[]{end_char});
else if( start_char != 0 )
return new String(new char[]{start_char});
else return null; // should not happen
}
/**
* For decoding instructions, see the i2b2 documentation CRC_Design.pdf
* The vital cd can be one or two characters.
* This implementation is more failsafe by using the following
* algorithm:
* <ol>
* <li>For {@code null} or {@code ""} use both timestamps accurate to day
* <li>Try to decode first character as end indicator</li>
* <li>If {@code vital_cd.length > 1} use second character as start indicator, otherwise if unable to decode the end indicator, use the first character.</li>
* </ol>
* @param vital_cd code to indicate accuracy of start and end date
*/
public void setActiveStatusCd(String vital_cd){
Visit visit = this;
// load accuracy
char endIndicator = 0;
char startIndicator = 0;
if( vital_cd == null || vital_cd.length() == 0 ){
// start and end date accurate to day
// leave indicators at 0/null
}else{
// load first indicator character
endIndicator = vital_cd.charAt(0);
}
ChronoUnit accuracy = null;
// end date indicator
switch( endIndicator ){
case 'U': // unknown, no date
case 'O': // ongoing, no date
// set to null
visit.setEndTime(null);
break;
case 0:
case 'Y': // known, accurate to day
accuracy = ChronoUnit.DAYS;
break;
case 'M': // known, accurate to month
accuracy = ChronoUnit.MONTHS;
break;
case 'X': // known, accurate to year
accuracy = ChronoUnit.YEARS;
break;
case 'R': // known, accurate to hour
accuracy = ChronoUnit.HOURS;
break;
case 'T': // known, accurate to minute
accuracy = ChronoUnit.MINUTES;
break;
case 'S': // known, accurate to second
accuracy = ChronoUnit.SECONDS;
break;
default:
// no end indicator means accurate to day
accuracy = ChronoUnit.DAYS;
// no match for end date -> check for start status in first character
startIndicator = endIndicator;
}
// set accuracy for end time
if( visit.getEndTime() != null && accuracy != null ){
visit.getEndTime().setAccuracy(accuracy);
}
// load start indicator
if( vital_cd != null && vital_cd.length() > 1 ){
// use second character, if available
startIndicator = vital_cd.charAt(1);
}// otherwise, the first character is used if end indicator wasn't used. See default case above
accuracy = null;
// start date indicator
switch( startIndicator ){
case 'L': // unknown, no date
case 'A': // active, no date
setStartTime(null);
break;
case 0: // same as D
case 'D': // known, accurate to day
accuracy = ChronoUnit.DAYS;
break;
case 'B': // known, accurate to month
accuracy = ChronoUnit.MONTHS;
break;
case 'F': // known, accurate to year
accuracy = ChronoUnit.YEARS;
break;
case 'H': // known, accurate to hour
accuracy = ChronoUnit.HOURS;
break;
case 'I': // known, accurate to minute
accuracy = ChronoUnit.MINUTES;
break;
case 'C': // known, accurate to second
accuracy = ChronoUnit.SECONDS;
break;
default: // default to days if unable to parse
accuracy = ChronoUnit.DAYS;
}
if( visit.getStartTime() != null && accuracy != null ){
visit.getStartTime().setAccuracy(accuracy);
}
}
public String getInOutCd(){
Visit patient = this;
if( patient.getStatus() == null )return null;
else switch( patient.getStatus() ){
case Inpatient:
return "I";
case Outpatient:
case Emergency: // unsupported by i2b2, map to outpatient
return "O";
default:
// XXX should not happen, warning
return null;
}
}
}
......@@ -35,6 +35,7 @@ import de.sekmi.histream.impl.VisitImpl;
* @author Raphael
*
*/
@Deprecated
public class I2b2Visit extends VisitImpl {
/**
......
......@@ -40,6 +40,7 @@ import java.util.logging.Logger;
import de.sekmi.histream.DateTimeAccuracy;
import de.sekmi.histream.Extension;
import de.sekmi.histream.Observation;
import de.sekmi.histream.ext.ExternalSourceType;
import de.sekmi.histream.ext.Patient;
......@@ -652,5 +653,9 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
public Class<I2b2Patient> getSlotType() {
return I2b2Patient.class;
}
@Override
public <U> U extractSubtype(I2b2Patient slotInstance, Class<U> subtype) {
return Extension.extractSupertype(slotInstance, subtype);
}
}
......@@ -40,6 +40,7 @@ import java.util.logging.Logger;
import de.sekmi.histream.DateTimeAccuracy;
import de.sekmi.histream.Extension;
import de.sekmi.histream.Observation;
import de.sekmi.histream.ext.ExternalSourceType;
import de.sekmi.histream.ext.Patient;
......@@ -62,6 +63,7 @@ import de.sekmi.histream.ext.Visit.Status;
* @author marap1
*
*/
@Deprecated
public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements Closeable{
private static final Logger log = Logger.getLogger(PostgresVisitStore.class.getName());
private static final Class<?>[] INSTANCE_TYPES = new Class[] {Visit.class,I2b2Visit.class};
......@@ -572,5 +574,9 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements
public Class<I2b2Visit> getSlotType() {
return I2b2Visit.class;
}
@Override
public <U> U extractSubtype(I2b2Visit slotInstance, Class<U> subtype) {
return Extension.extractSupertype(slotInstance, subtype);
}
}
......@@ -10,6 +10,7 @@ import org.junit.Test;
import de.sekmi.histream.DateTimeAccuracy;
@Deprecated
public class TestI2b2Visit {
private DateTimeAccuracy createAccurateTimestamp(){
......
package de.sekmi.histream.i2b2;
/*
* #%L
* histream
* %%
* Copyright (C) 2013 - 2015 R.W.Majeed
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import java.io.IOException;
import java.sql.SQLException;
import java.time.Instant;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import de.sekmi.histream.ext.ExternalSourceType;
import de.sekmi.histream.impl.ExternalSourceImpl;
public class TestPostgresVisitCache {
PostgresPatientVisitCache store;
LocalHSQLDataSource ds;
@Before