Commit fe604cf4 authored by R.W.Majeed's avatar R.W.Majeed

initial commit

parents
.settings/
.classpath
.project
target/
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>de.sekmi.histream</groupId>
<artifactId>histream</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<!-- assembly plugin to specify main class for runnable jar -->
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.5.4</version>
<configuration>
<archive>
<manifest>
<mainClass>de.sekmi.histream.impl.HIStream</mainClass>
</manifest>
</archive>
</configuration>
</plugin>
<!--
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>2.6</version>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>-->
<plugin>
<!-- collect dependencies -->
<!-- use mvn dependency:copy-dependencies to execute -->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<!-- configure the plugin here -->
<!--<outputDirectory>
${project.build.directory}/dependencies
</outputDirectory>-->
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-xjc-plugin</artifactId>
<version>3.0.2</version>
<configuration>
<!--<extensions>
<extension>org.apache.cxf.xjcplugins:cxf-xjc-dv:2.3.0</extension>
</extensions>-->
</configuration>
<executions>
<execution>
<id>generate-sources</id>
<phase>generate-sources</phase>
<goals>
<goal>xsdtojava</goal>
</goals>
<configuration>
<sourceRoot>${basedir}/target/generated-sources/xjc</sourceRoot>
<xsdOptions>
<xsdOption>
<xsd>${basedir}/src/main/resources/histream-config.xsd</xsd>
<packagename>de.sekmi.histream.config</packagename>
<extensionArgs>
<arg>-encoding</arg>
<arg>${project.build.sourceEncoding}</arg>
</extensionArgs>
</xsdOption>
</xsdOptions>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>add-source</id>
<phase>generate-sources</phase>
<goals>
<goal>add-source</goal>
</goals>
<configuration>
<sources>
<source> ${project.build.directory}/generated-sources/xjc</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
<reporting>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.9.1</version>
</plugin>
</plugins>
</reporting>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>9.4-1201-jdbc41</version>
</dependency>
</dependencies>
</project>
\ No newline at end of file
SET PATH=%PATH%;%USERPROFILE%\Tools\apache-maven-3.2.3\bin
SET JAVA_HOME=C:\Program Files\Java\jdk1.7.0_17
IF EXIST "%JAVA_HOME%" GOTO OK
SET JAVA_HOME=c:\Program Files\Java\jdk1.8.0_25
IF EXIST "%JAVA_HOME%" GOTO OK
SET JAVA_HOME=C:\Program Files\Java\jdk1.6.0_35
IF EXIST "%JAVA_HOME%" GOTO OK
ECHO JAVA_HOME not found!
PAUSE
EXIT
:OK
REM cd psd
START CMD /K
package de.sekmi.histream;
import javax.xml.bind.annotation.XmlEnum;
/**
*
* @author marap1
*
* Typical values sent via HL7 at the university hospital in Giessen (total count during observation period):
* <li>L: 60000
* <li>LL: 24000
* <li>H: 43000
* <li>HH: 43000
* <li>*: 250
* <li>**: 15
* <li>N: 23000
* <li>(empty/space): 14000
*/
@XmlEnum
public enum AbnormalFlag{
/**
* Abnormal (applies to non-numeric results)
*/
Abnormal ("A"),
/**
* Very abnormal (applies to non-numeric units, analogous to panic limits for numeric units)
*/
AbnormalOutsidePanicLimits ("AA"),
/**
* Above high normal
*/
AboveHighNormal ("H"),
/**
* Above upper panic limits
*/
AboveUpperPanicLimits ("HH"),
/**
* Above absolute high-off instrument scale
*/
HighOff (">"),
/**
* Below absolute low-off instrument scale
*/
LowOff ("<"),
/**
* Below low normal
*/
BelowNormal ("L"),
/**
* Below lower panic limits
*/
BelowNormalPanicLimits ("LL"),
/**
* Better--use when direction not relevant
*/
Better ("B"),
/**
* Normal
*/
Normal ("N"),
/**
* Significant change down
*/
DownSignificantly ("D"),
/**
* Significant change up
*/
UpSignificantly ("U"),
/**
* Worse--use when direction not relevant
*/
Worse ("W");
String key;
private AbnormalFlag(String key){
this.key = key;
}
public String key(){return this.key;}
/**
* Get abnormal flag for the specified key.
* @param key
* @return null if key is null, matching flag otherwise
* @throws IllegalArgumentException for non-null keys which do not match any enum constant.
*/
public static final AbnormalFlag fromKey(String key)throws IllegalArgumentException{
if( key == null )return null;
for( AbnormalFlag v : AbnormalFlag.values() ){
if( key.equals(v.key) )return v;
}
throw new IllegalArgumentException();
}
}
\ No newline at end of file
package de.sekmi.histream;
public interface ConceptValuePair {
public String getConceptId();
public Value getValue();
}
package de.sekmi.histream;
import java.time.LocalDateTime;
import java.time.temporal.ChronoField;
import java.time.temporal.ChronoUnit;
import java.time.temporal.Temporal;
import java.time.temporal.TemporalField;
import java.time.temporal.TemporalUnit;
import java.util.Date;
/**
* Local date and time with specified accuracy. Maximum resolution is seconds.
* Supported accuracy values are {@link ChronoUnit#YEARS}, {@link ChronoUnit#MONTHS},
* {@link ChronoUnit#DAYS}, {@link ChronoUnit#HOURS}, {@link ChronoUnit#MINUTES} and
* {@link ChronoUnit#SECONDS}.
* @author Raphael
*
*/
public class DateTimeAccuracy implements Temporal {
private LocalDateTime dateTime;
private ChronoUnit accuracy;
/**
* Create date time with accuracy to seconds.
* @param dateTime
*/
public DateTimeAccuracy(LocalDateTime dateTime){
this.dateTime = dateTime;
this.accuracy = ChronoUnit.SECONDS;
}
public DateTimeAccuracy(int year) {
dateTime = LocalDateTime.of(year, 1, 1, 0, 0);
accuracy = ChronoUnit.YEARS;
// truncation works only up to days
dateTime.truncatedTo(ChronoUnit.DAYS);
}
public DateTimeAccuracy(int year, int month) {
dateTime = LocalDateTime.of(year, month, 1, 0, 0);
accuracy = ChronoUnit.MONTHS;
// truncation works only up to days
dateTime.truncatedTo(ChronoUnit.DAYS);
}
public DateTimeAccuracy(int year, int month, int day) {
dateTime = LocalDateTime.of(year, month, day, 0, 0);
accuracy = ChronoUnit.DAYS;
dateTime.truncatedTo(accuracy);
}
public DateTimeAccuracy(int year, int month, int day, int hours) {
dateTime = LocalDateTime.of(year, month, day, hours, 0);
accuracy = ChronoUnit.HOURS;
dateTime.truncatedTo(accuracy);
}
public DateTimeAccuracy(int year, int month, int day, int hours, int mins) {
dateTime = LocalDateTime.of(year, month, day, hours, mins);
accuracy = ChronoUnit.MINUTES;
dateTime.truncatedTo(accuracy);
}
public DateTimeAccuracy(int year, int month, int day, int hours, int mins, int secs) {
dateTime = LocalDateTime.of(year, month, day, hours, mins, secs);
accuracy = ChronoUnit.SECONDS;
dateTime.truncatedTo(accuracy);
}
// Temporal interface behaves like undelaying dateTime
@Override
public long getLong(TemporalField arg0) {return dateTime.getLong(arg0);}
@Override
public boolean isSupported(TemporalField arg0) {return dateTime.isSupported(arg0);}
@Override
public boolean isSupported(TemporalUnit unit) {return dateTime.isSupported(unit);}
@Override
public Temporal plus(long amountToAdd, TemporalUnit unit) {return dateTime.plus(amountToAdd,unit);}
@Override
public long until(Temporal endExclusive, TemporalUnit unit) {return dateTime.until(endExclusive, unit);}
@Override
public Temporal with(TemporalField field, long newValue) {return dateTime.with(field,newValue);}
/**
* Get the accuracy for the date time object.
* @return accuracy
*/
public ChronoUnit getAccuracy(){return accuracy;}
/**
* Set the accuracy for the date time object.
* TODO: what happens if the accuracy is increased (but the underlaying time was truncated)
* @param accuracy
*/
public void setAccuracy(ChronoUnit accuracy){
this.accuracy = accuracy;
//
}
/**
* Get the local time
* @return
*/
public LocalDateTime getLocal(){ return dateTime; }
public void set(Date timestamp, ChronoUnit accuracy){
dateTime = LocalDateTime.from(timestamp.toInstant());
dateTime.truncatedTo(accuracy);
this.accuracy = accuracy;
}
@Override
public String toString(){
return toPartialIso8601();
}
/**
* Append exactly {@code digits} to the {@code builder}. Prefix with zeros if necessary.
* @param builder builder to append to
* @param field field to add
* @param digits digits to add
*/
private void appendWithZeroPrefix(StringBuilder builder, TemporalField field, int digits){
int v = dateTime.get(field);
for(int i=digits; i>1 && v<(10^(i-1)); i--){
builder.append('0');
}
builder.append(v);
}
/**
* Convert the date to a partial ISO 8601 date time string.
* Information up to {@link #getAccuracy()}} is used for the
* string representation.
* @return partial date.
*/
public String toPartialIso8601(){
StringBuilder b = new StringBuilder(20);
if( dateTime == null )return "null";
char[] prefixes = {0,'-','-','T',':',':'};
ChronoField[] fields = {ChronoField.YEAR, ChronoField.MONTH_OF_YEAR, ChronoField.DAY_OF_MONTH, ChronoField.HOUR_OF_DAY, ChronoField.MINUTE_OF_HOUR, ChronoField.SECOND_OF_MINUTE};
int[] digits = {4,2,2,2,2,2};
for( int i=0; i<fields.length; i++ ){
if( prefixes[i] != 0 )b.append(prefixes[i]);
appendWithZeroPrefix(b, fields[i], digits[i]);
if( accuracy == fields[i].getBaseUnit() )break;
}
return b.toString();
}
/**
* Parses a partial ISO 8601 date time string.
* [-]CCYY-MM-DDThh:mm:ss[Z|(+|-)hh:mm]
* @param str
* @return date time with accuracy as derived from parse
* @throws IllegalArgumentException
*/
public static DateTimeAccuracy parsePartialIso8601(String str)throws IllegalArgumentException{
if( str.length() < 4 )throw new IllegalArgumentException("Need at least 4 characters for year: "+str);
// parse year
int year = Integer.parseInt(str.substring(0, 4));
if( str.length() == 4 ){ // specified to accuracy of years
return new DateTimeAccuracy(year);
}else if( str.length() < 7 || str.charAt(4) != '-' ){
throw new IllegalArgumentException("Expected YYYY-MM");
}
// parse month
int month = Integer.parseInt(str.substring(5, 7));
if( str.length() == 7 ){ // specified to accuracy of months
return new DateTimeAccuracy(year, month);
}else if( str.length() < 10 || str.charAt(7) != '-' ){
throw new IllegalArgumentException("Expected YYYY-MM-DD");
}
// parse day
int day = Integer.parseInt(str.substring(8, 10));
if( str.length() == 10 ){ // specified to accuracy of days
return new DateTimeAccuracy(year, month, day);
}else if( str.length() < 13 || str.charAt(10) != 'T' ){
throw new IllegalArgumentException("Expected yyyy-mm-ddThh");
}
// parse hours
int hours = Integer.parseInt(str.substring(11, 13));
if( str.length() == 13 ){ // specified to accuracy of hours
return new DateTimeAccuracy(year, month, day, hours);
}else if( str.length() < 16 || str.charAt(13) != ':' ){
throw new IllegalArgumentException("Expected yyyy-mm-ddThh:mm");
}
// parse minutes
int mins = Integer.parseInt(str.substring(14, 16));
if( str.length() == 16 ){ // specified to accuracy of minutes
return new DateTimeAccuracy(year, month, day, hours, mins);
}else if( str.length() < 19 || str.charAt(16) != ':' ){
throw new IllegalArgumentException("Expected yyyy-mm-ddThh:mm:ss");
}
// parse seconds
int secs = Integer.parseInt(str.substring(17, 19));
if( str.length() == 19 ){ // specified to accuracy of seconds
return new DateTimeAccuracy(year, month, day, hours, mins, secs);
}else if( str.length() < 19 || str.charAt(16) != ':' ){
throw new IllegalArgumentException("Expected yyyy-mm-ddThh:mm:ss");
}
throw new UnsupportedOperationException("Timezone support not implemented yet");
}
}
package de.sekmi.histream;
/**
* Extensions allow additional information to be stored and retrieved
* for observations.
*
* @author Raphael
*
* @param <T>
*/
public interface Extension<T>{
/**
* Creates a new instance for the given observation. This is only called
* once for each observation, usually when the extension requested for
* the observation. The instance is then cached automatically.
*
* @param observation
* @return extension
*/
T createInstance(Observation observation);
/**
* Creates a static instance which is independent of a given observation.
* <p>
* Some extensions do not support independent instances. In this case, all
* calls to this method will result in an {@link UnsupportedOperationException}.
* @return new instance
* @throws UnsupportedOperationException if instance creation without {@link Observation} is not possible.
*/
T createInstance() throws UnsupportedOperationException;
/**
* Get class of the instance type. Should be a basic interface like Patient, Visit, Location, Concept, etc.
* TODO change return type to array, to register all compatible classes
* @return instance type
*/
Class<?>[] getInstanceTypes();
}
package de.sekmi.histream;
public interface ExtensionAccessor<T> {
/**
* Get the extension type instance. The instance is created automatically on first access.
* @param observation
* @return
*/
T access(Observation observation);
void set(Observation observation, T ext);
// TODO: if necessary, create method isAvailable which does not create the instance automatically
}
package de.sekmi.histream;
public interface Modifier extends ConceptValuePair{
@Override
String getConceptId();
@Override
Value getValue();
void setValue(Value value);
}
package de.sekmi.histream;
import java.util.Enumeration;
import de.sekmi.histream.ext.ExternalSourceType;
public interface Observation extends ConceptValuePair, ExternalSourceType{
String getPatientId();
String getEncounterId();
String getProviderId();
String getLocationId();
@Override
String getConceptId();
@Override
Value getValue();
void setValue(Value value);
DateTimeAccuracy getStartTime();
DateTimeAccuracy getEndTime();
void setEndTime(DateTimeAccuracy date);
ObservationFactory getFactory();
<T> T getExtension(Class<T> extensionType) throws IllegalArgumentException;
<T> void setExtension(Class<T> extensionType, T extension) throws IllegalArgumentException;
void setEncounterId(String encounterId);
boolean hasModifiers();
Modifier getModifier(String modifierId);
Enumeration<Modifier> getModifiers();
Modifier addModifier(String modifierId)throws IllegalArgumentException;
}
package de.sekmi.histream;
/**
* Single instance which generates all observations.
* Manages extensions which enhance/annotate observations.
*
* @author marap1
*
*/
public interface ObservationFactory {
/**
* Register an extension. Registered extensions cannot be removed.
* @param extension
*/
<T> void registerExtension(Extension<T> extension);
/**
* Get a list of currently registered extensions
* @return currently registered extensions
*/
//Iterable<Extension<?>> registeredExtensions();
/**
* Extensions can be accessed through either via {@link Observation#getExtension(Class)}
* or via an {@link ExtensionAccessor}. The latter method is faster, because no hash
* lookup of the extensionType needs to be performed.
*
* @param extensionType type to get an accessor for
* @return extension accessor or null if the extension is not available
*/
<T> ExtensionAccessor<T> getExtensionAccessor(Class<T> extensionType);
/**
* Create a new observation
* @param patientId
* @param conceptId
* @param startTime TODO