...
 
Commits (5)
......@@ -26,9 +26,9 @@ package de.sekmi.histream;
* Extensions allow additional information to be stored and retrieved
* for observations.
*
* @author Raphael
* @author R.W.Majeed
*
* @param <T> type class
* @param <T> slot type to be kept in memory with the observation
*/
public interface Extension<T>{
/**
......@@ -58,6 +58,23 @@ public interface Extension<T>{
* TODO change return type to array, to register all compatible classes
* @return instance type
*/
Iterable<Class<? super T>> getInstanceTypes();
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;
......
package de.sekmi.histream.impl;
import java.util.Arrays;
/*
* #%L
* histream
......@@ -35,11 +32,12 @@ import de.sekmi.histream.ext.Patient;
* @author R.W.Majeed
*
*/
@Deprecated
public class SimplePatientExtension implements Extension<PatientImpl>{
private final static Iterable<Class<? super PatientImpl>> TYPES = Arrays.asList(Patient.class, PatientImpl.class);
private final static Class<?>[] TYPES = new Class[] {Patient.class, PatientImpl.class};
@Override
public Iterable<Class<? super PatientImpl>> getInstanceTypes() {return TYPES;}
public Class<?>[] getInstanceTypes() {return TYPES;}
@Override
public PatientImpl createInstance(Object... args) {
......@@ -63,4 +61,14 @@ public class SimplePatientExtension implements Extension<PatientImpl>{
return patient;
}
@Override
public Class<PatientImpl> getSlotType() {
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);
}
}
}
......@@ -23,18 +23,18 @@ package de.sekmi.histream.impl;
import de.sekmi.histream.Observation;
import java.util.Arrays;
import de.sekmi.histream.Extension;
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 Iterable<Class<? super VisitImpl>> TYPES = Arrays.asList(Visit.class, VisitImpl.class);
private final static Class<?>[] TYPES = new Class[] {Visit.class, VisitImpl.class};
@Override
public Iterable<Class<? super VisitImpl>> getInstanceTypes() {return TYPES;}
public Class<?>[] getInstanceTypes() {return TYPES;}
@Override
public VisitImpl createInstance(Object... args) {
......@@ -65,4 +65,13 @@ public class SimpleVisitExtension implements Extension<VisitImpl>{
return visit;
}
@Override
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;
}
}
......@@ -32,6 +32,15 @@ public class TestFileObservationSuppliers {
s.close();
p.close();
}
@Test
public void verifyMinimalXML() throws Exception{
GroupedXMLProvider p = new GroupedXMLProvider(null);
ObservationSupplier s = p.createSupplier(getClass().getResourceAsStream("/min.xml"), factory);
Assert.assertTrue( s.stream().count() > 0 );
s.close();
p.close();
}
@Test
public void verifyFlatSupplier() throws Exception{
FlatProviderFactory p = new FlatProviderFactory(null);
......
<eav-data xmlns="http://sekmi.de/histream/ns/eav-data"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<!-- chronologisch impliziert, dass der zeitstempel eines nachfolgenden elementes
größer als alle vorangehenden elemente sein muss. Der Zeitstempel kann vor dem
Encounter-Start liegen -->
<meta>
<!-- Zeitpunkt, an dem der Export erstellt wurde bzw. Datenstand -->
<etl-strategy>replace-visit</etl-strategy>
<source timestamp="2015-04-21T06:58:00Z" id="test"/>
</meta>
<patient id="XX12345">
<given-name>A B</given-name>
<surname>Dampf</surname>
<!-- Gender allows for female, male, indeterminate -->
<!-- Gender element can also be removed or left empty -->
<gender>female</gender>
<encounter id="XXE12345">
<start>2014-01-01T10:30:00</start>
<fact concept="T:date:secs" start="2014-09-07T10:40:03"/>
</encounter>
</patient>
</eav-data>
......@@ -17,11 +17,6 @@
</plugins>
</build>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
......@@ -33,5 +28,17 @@
<artifactId>histream-core</artifactId>
<version>0.16-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hsqldb</groupId>
<artifactId>hsqldb</artifactId>
<version>2.4.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
\ No newline at end of file
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 {
/**
......@@ -73,6 +74,8 @@ public class I2b2Visit extends VisitImpl {
// 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");
}
}
......
......@@ -30,7 +30,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
......@@ -41,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;
......@@ -74,7 +74,7 @@ import de.sekmi.histream.ext.PatientStore;
*/
public class PostgresPatientStore extends PostgresExtension<I2b2Patient> implements PatientStore, Closeable{
private static final Logger log = Logger.getLogger(PostgresPatientStore.class.getName());
private static final Iterable<Class<? super I2b2Patient>> INSTANCE_TYPES = Arrays.asList(Patient.class, I2b2Patient.class);
private static final Class<?>[] INSTANCE_TYPES = new Class[] {Patient.class, I2b2Patient.class};
private String projectId;
private String idSourceDefault;
private char idSourceSeparator;
......@@ -541,7 +541,7 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
}
@Override
public Iterable<Class<? super I2b2Patient>> getInstanceTypes() {
public Class<?>[] getInstanceTypes() {
return INSTANCE_TYPES;
}
......@@ -649,5 +649,13 @@ public class PostgresPatientStore extends PostgresExtension<I2b2Patient> impleme
db = null;
}
}
@Override
public Class<I2b2Patient> getSlotType() {
return I2b2Patient.class;
}
@Override
public <U> U extractSubtype(I2b2Patient slotInstance, Class<U> subtype) {
return Extension.extractSupertype(slotInstance, subtype);
}
}
......@@ -30,7 +30,6 @@ import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Iterator;
......@@ -41,8 +40,10 @@ 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;
import de.sekmi.histream.ext.StoredExtensionType;
import de.sekmi.histream.ext.Visit;
import de.sekmi.histream.ext.Visit.Status;
......@@ -62,9 +63,10 @@ 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 Iterable<Class<? super I2b2Visit>> INSTANCE_TYPES = Arrays.asList(Visit.class,I2b2Visit.class);
private static final Class<?>[] INSTANCE_TYPES = new Class[] {Visit.class,I2b2Visit.class};
private String projectId;
private int maxEncounterNum;
......@@ -476,9 +478,8 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements
return getOrCreateInstance(fact.getEncounterId(), fact.getExtension(I2b2Patient.class), fact.getSource());
}
@Override
public Iterable<Class<? super I2b2Visit>> getInstanceTypes() {
return INSTANCE_TYPES;
public I2b2Visit createInstance(String encounterId, Patient patient, ExternalSourceType source) {
return getOrCreateInstance(encounterId, (I2b2Patient)patient, source);
}
/**
......@@ -565,5 +566,17 @@ public class PostgresVisitStore extends PostgresExtension<I2b2Visit> implements
db = null;
}
}
@Override
public Class<?>[] getInstanceTypes() {
return INSTANCE_TYPES;
}
@Override
public Class<I2b2Visit> getSlotType() {
return I2b2Visit.class;
}
@Override
public <U> U extractSubtype(I2b2Visit slotInstance, Class<U> subtype) {
return Extension.extractSupertype(slotInstance, subtype);
}
}
package de.sekmi.histream.i2b2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.sql.Statement;
import java.util.logging.Logger;
import javax.sql.DataSource;
// TODO move to query-i2b2-sql
public class LocalHSQLDataSource implements DataSource{
private PrintWriter pw;
private static final String JDBC_URI = "jdbc:hsqldb:file:target/testdb_crcdata";
public void delete() throws SQLException {
try( Connection c = getConnection() ){
Statement s = c.createStatement();
s = c.createStatement();
s.executeQuery("DROP SCHEMA PUBLIC CASCADE");
s.close();
}
}
private static void executeSQL(Connection dbc, BufferedReader lines) throws SQLException, IOException {
StringBuilder stmt = new StringBuilder();
String line;
while( (line = lines.readLine()) != null ) {
line = line.trim();
if( line.trim().startsWith("--") ) {
// ignore comment lines
continue;
}
if( line.endsWith(";") ) {
// append without ;
stmt.append(line.substring(0, line.length()-1));
// execute
try( Statement s = dbc.createStatement() ){
s.executeUpdate(stmt.toString());
}
// clear
stmt = new StringBuilder();
}else {
stmt.append(line);
}
}
}
/**
* Create the database and initialize it with the specified DDL statements
* @param sql_ddl SQL DDL statements
* @throws SQLException SQL error
* @throws IOException IO error reading the DDL
*/
public void create(BufferedReader ... sql_ddl) throws SQLException, IOException {
try( Connection dbc = DriverManager.getConnection(JDBC_URI+";create=true", "SA", "") ){
for( BufferedReader ddl : sql_ddl ) {
executeSQL(dbc, ddl);
}
}
}
public void createI2b2() throws SQLException, IOException {
try( InputStream in = TestHSQLDataSource.class.getResourceAsStream("/i2b2_hsqldb_ddl_create.sql");
BufferedReader rd = new BufferedReader(new InputStreamReader(in, StandardCharsets.UTF_8)) )
{
this.create(rd);
}
}
public Integer executeCountQuery(String sql) throws SQLException {
Integer ret = null;
try( Connection c = getConnection();
Statement s = c.createStatement();
ResultSet rs = s.executeQuery(sql) )
{
if( rs.next() ) {
ret = rs.getInt(1);
}
}
return ret;
}
public LocalHSQLDataSource() {
pw = new PrintWriter(System.out);
}
@Override
public PrintWriter getLogWriter() throws SQLException {
return pw;
}
@Override
public void setLogWriter(PrintWriter out) throws SQLException {
this.pw = out;
}
@Override
public void setLoginTimeout(int seconds) throws SQLException {
// TODO Auto-generated method stub
}
@Override
public int getLoginTimeout() throws SQLException {
// TODO Auto-generated method stub
return 0;
}
@Override
public Logger getParentLogger() throws SQLFeatureNotSupportedException {
throw new SQLFeatureNotSupportedException();
}
@Override
public <T> T unwrap(Class<T> iface) throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public boolean isWrapperFor(Class<?> iface) throws SQLException {
throw new UnsupportedOperationException();
}
@Override
public Connection getConnection() throws SQLException {
return getConnection("SA","");
}
@Override
public Connection getConnection(String username, String password) throws SQLException {
return DriverManager.getConnection(JDBC_URI+";ifexists=true", username, password);
}
}
package de.sekmi.histream.i2b2;
import java.io.IOException;
import java.sql.SQLException;
import org.junit.Assert;
import org.junit.Test;
public class TestHSQLDataSource {
@Test
public void testCreateTables() throws IOException, SQLException {
LocalHSQLDataSource ds = new LocalHSQLDataSource();
ds.createI2b2();
// perform queries
Assert.assertEquals(0, ds.executeCountQuery("SELECT COUNT(*) FROM observation_fact").intValue());
// drop database
ds.delete();
}
}
......@@ -10,6 +10,7 @@ import org.junit.Test;
import de.sekmi.histream.DateTimeAccuracy;
@Deprecated
public class TestI2b2Visit {
private DateTimeAccuracy createAccurateTimestamp(){
......
......@@ -25,11 +25,54 @@ import java.io.Closeable;
import java.io.IOException;
import java.sql.DriverManager;
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.i2b2.PostgresPatientStore;
import de.sekmi.histream.impl.ExternalSourceImpl;
public class TestPostgresPatientStore implements Closeable {
PostgresPatientStore store;
LocalHSQLDataSource ds;
@Before
public void createDatabase() throws SQLException, IOException {
ds = new LocalHSQLDataSource();
ds.delete();
ds.createI2b2();
}
private void openPatientStore() throws SQLException {
store = new PostgresPatientStore();
store.open(ds.getConnection(), "test", new DataDialect());
}
@After
public void cleanupDatabase() throws SQLException, IOException {
if( store != null ) {
store.close();
}
ds.delete();
}
@Test
public void insertPatient() throws IOException, SQLException {
ExternalSourceType t = new ExternalSourceImpl("junit", Instant.now());
openPatientStore();
store.createInstance("ABC001", t);
store.flush();
store.close();
store = null;
// reload store from database
openPatientStore();
// attempt to load previously stored patient
I2b2Patient p = store.retrieve("ABC001");
Assert.assertNotNull(p);
}
public void open(String host, int port, String user, String password, String projectId) throws ClassNotFoundException, SQLException{
store = new PostgresPatientStore();
store.open(DriverManager.getConnection("jdbc:postgresql://"+host+":"+port+"/i2b2", user, password),projectId, new DataDialect());
......
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
public void createDatabase() throws SQLException, IOException {
ds = new LocalHSQLDataSource();
ds.delete();
ds.createI2b2();
}
private void loadCache() throws SQLException {
store = new PostgresPatientVisitCache();
store.open(ds.getConnection(), "test", new DataDialect());
}
@After
public void cleanupDatabase() throws SQLException, IOException {
if( store != null ) {
store.close();
}
ds.delete();
}
@Test
public void insertPatient() throws IOException, SQLException {
ExternalSourceType t = new ExternalSourceImpl("junit", Instant.now());
loadCache();
store.createPatient("ABC001", t);
store.close();
store = null;
// reload store from database
loadCache();
// attempt to load previously stored patient
I2b2Patient p = store.lookupPatientId("ABC001");
Assert.assertNotNull(p);
}
@Test
public void insertPatientVisitCombo() throws IOException, SQLException {
ExternalSourceType t = new ExternalSourceImpl("junit", Instant.now());
loadCache();
I2b2Patient pat = store.createPatient("PAT001", t);
store.createPatient("PAT002", t);
store.createVisit("ENC001", pat, t);
store.createVisit("ENC002", pat, t);
store.close();
store = null;
// reload store from database
loadCache();
// attempt to load previously stored patient
I2b2PatientVisit v = store.findVisit("ENC001");
Assert.assertNotNull(v);
Assert.assertNotNull(v.getPatient());
Assert.assertEquals("PAT001", v.getPatientId());
}
}
......@@ -25,15 +25,77 @@ import java.io.Closeable;
import java.io.IOException;
import java.sql.DriverManager;
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.DateTimeAccuracy;
import de.sekmi.histream.ext.ExternalSourceType;
import de.sekmi.histream.i2b2.PostgresVisitStore;
import de.sekmi.histream.impl.ExternalSourceImpl;
public class TestPostgresVisitStore implements Closeable {
private PostgresVisitStore store;
private PostgresVisitStore vs;
private PostgresPatientStore ps;
LocalHSQLDataSource ds;
@Before
public void createDatabase() throws SQLException, IOException {
ds = new LocalHSQLDataSource();
ds.delete();
ds.createI2b2();
}
private void openVisitStore() throws SQLException {
ps = new PostgresPatientStore();
ps.open(ds.getConnection(), "test", new DataDialect());
vs = new PostgresVisitStore();
vs.open(ds.getConnection(), "test", new DataDialect());
}
@After
public void cleanupDatabase() throws SQLException, IOException {
if( vs != null ) {
vs.close();
}
if( ps != null ) {
ps.close();
}
ds.delete();
}
@Test
public void insertVisit() throws IOException, SQLException {
ExternalSourceType t = new ExternalSourceImpl("junit", Instant.now());
openVisitStore();
// create patient
I2b2Patient pat = ps.createInstance("ABC001", t);
I2b2Visit v = vs.createInstance("VIS001", pat, t);
v.setStartTime(new DateTimeAccuracy(Instant.now()));
v = vs.findVisit("VIS001");
Assert.assertEquals("ABC001",v.getPatientId());
// TODO reload store from database
vs.close();
vs = null;
ps.close();
ps = null;
// attempt to load previously stored patient
openVisitStore();
v = vs.findVisit("VIS001");
Assert.assertNotNull(v);
// does not work in current versions of histream
// Assert.assertEquals("ABC001",v.getPatientId());
}
public void open(String host, int port) throws ClassNotFoundException, SQLException{
store = new PostgresVisitStore();
store.open(DriverManager.getConnection("jdbc:postgresql://"+host+":"+port+"/i2b2", "i2b2demodata", "demodata"), "demo", new DataDialect());
vs = new PostgresVisitStore();
vs.open(DriverManager.getConnection("jdbc:postgresql://"+host+":"+port+"/i2b2", "i2b2demodata", "demodata"), "demo", new DataDialect());
}
private void open()throws Exception{
......@@ -42,15 +104,15 @@ public class TestPostgresVisitStore implements Closeable {
@Override
public void close() throws IOException{
store.close();
vs.close();
}
public PostgresVisitStore getStore(){ return store; }
public PostgresVisitStore getStore(){ return vs; }
public static void main(String args[]) throws Exception{
TestPostgresVisitStore t = new TestPostgresVisitStore();
t.open();
System.out.println("Current visit cache size: "+t.store.size());
System.out.println("Current visit cache size: "+t.vs.size());
t.close();
}
}
-- demo data for testing query executions via SQL
INSERT INTO patient_dimension(patient_num,vital_status_cd,birth_date,sex_cd)VALUES(1001, NULL, '2001-01-01','F');
INSERT INTO patient_dimension(patient_num,vital_status_cd,birth_date,sex_cd)VALUES(1002, NULL, '2002-02-02','M');
INSERT INTO patient_dimension(patient_num,vital_status_cd,birth_date,sex_cd)VALUES(1003, NULL, '2003-03-03','F');
INSERT INTO visit_dimension(encounter_num,patient_num,active_status_cd,start_date)VALUES(2001, 1001, NULL, '2011-01-01 01:00:00');
INSERT INTO visit_dimension(encounter_num,patient_num,active_status_cd,start_date)VALUES(2002, 1001, NULL, '2011-01-02 02:00:00');
INSERT INTO visit_dimension(encounter_num,patient_num,active_status_cd,start_date)VALUES(2003, 1001, NULL, '2011-01-03 03:00:00');
INSERT INTO visit_dimension(encounter_num,patient_num,active_status_cd,start_date)VALUES(2004, 1002, NULL, '2011-01-01 01:00:00');
INSERT INTO visit_dimension(encounter_num,patient_num,active_status_cd,start_date)VALUES(2005, 1002, NULL, '2011-01-02 02:00:00');
INSERT INTO observation_fact(encounter_num,patient_num,start_date,concept_cd)VALUES(2001,1001,'2011-01-01 01:01:00','ICD10GM:F30.0');
INSERT INTO observation_fact(encounter_num,patient_num,start_date,concept_cd)VALUES(2001,1001,'2011-01-01 01:02:00','ICD10GM:F30.8');
INSERT INTO observation_fact(encounter_num,patient_num,start_date,concept_cd)VALUES(2002,1001,'2011-01-02 01:01:00','ICD10GM:Y36.9!');
INSERT INTO observation_fact(encounter_num,patient_num,start_date,concept_cd)VALUES(2004,1002,'2011-01-01 01:01:00','ICD10GM:Y36.9!');
COMMIT;
-- demo data for testing query executions via SQL
CREATE TABLE observation_fact(
encounter_num INTEGER NOT NULL,
patient_num INTEGER NOT NULL,
concept_cd VARCHAR(50) NOT NULL,
provider_id VARCHAR(50) NULL,
start_date DATETIME NOT NULL,
modifier_cd VARCHAR(50) NULL,
instance_num INTEGER NULL,
valtype_cd VARCHAR(50) NULL,
tval_char VARCHAR(255) NULL,
nval_num DECIMAL(18,5) NULL,
units_cd VARCHAR(50) NULL,
end_date DATETIME NULL,
location_cd VARCHAR(50) NULL,
update_date DATETIME NULL,
download_date DATETIME NULL,
import_date DATETIME NULL,
sourcesystem_cd VARCHAR(50) NULL
);
CREATE TABLE patient_dimension(
patient_num INTEGER NOT NULL,
vital_status_cd VARCHAR(3) NULL,
birth_date DATETIME NULL,
death_date DATETIME NULL,
sex_cd VARCHAR(8) NULL,
update_date DATETIME NULL,
download_date DATETIME NULL,
import_date DATETIME NULL,
sourcesystem_cd VARCHAR(50) NULL
);
CREATE TABLE patient_mapping(
patient_ide VARCHAR(200) NOT NULL,
patient_ide_source VARCHAR(50) NOT NULL,
patient_num INTEGER NOT NULL,
patient_ide_status VARCHAR(50) NULL,
project_id VARCHAR(50) NOT NULL,
update_date DATETIME NULL,
download_date DATETIME NULL,
import_date DATETIME NULL,
sourcesystem_cd VARCHAR(50) NULL
);
CREATE TABLE visit_dimension(
encounter_num INTEGER NOT NULL,
patient_num INTEGER NOT NULL,
active_status_cd VARCHAR(3) NULL,
start_date DATETIME NULL,
end_date DATETIME NULL,
inout_cd VARCHAR(8) NULL,
location_cd VARCHAR(64) NULL,
update_date DATETIME NULL,
download_date DATETIME NULL,
import_date DATETIME NULL,
sourcesystem_cd VARCHAR(50) NULL
);
-- references to patient_ide, patient_ide_source NULL??
CREATE TABLE encounter_mapping(
encounter_ide VARCHAR(200) NOT NULL,
encounter_ide_source VARCHAR(50) NOT NULL,
project_id VARCHAR(50) NOT NULL,
encounter_num INTEGER NOT NULL,
patient_ide VARCHAR(200) NULL,
patient_ide_source VARCHAR(50) NULL,
encounter_ide_status VARCHAR(50) NULL,
update_date DATETIME NULL,
download_date DATETIME NULL,
import_date DATETIME NULL,
sourcesystem_cd VARCHAR(50) NULL
);
DROP TABLE observation_fact;
DROP TABLE patient_dimension;
DROP TABLE visit_dimension;