Commit aa194cef authored by R.W.Majeed's avatar R.W.Majeed
Browse files

JAXB fact provider uncoupled from old XML fact provider

parent 16c093a2
...@@ -17,9 +17,11 @@ ...@@ -17,9 +17,11 @@
limitations under the License. limitations under the License.
#L% #L%
--> -->
<dwh-eav xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" > <eav-data xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<!-- TODO namespace xmlns="http://sekmi.de/histream/dwh-eav..." --> <!-- TODO namespace xmlns="http://sekmi.de/histream/dwh-eav..." -->
<!-- chronologisch impliziert, dass der zeitstempel eines nachfolgenden elementes gr��er als alle vorangehenden elemente sein muss. Der Zeitstempel kann vor dem Encounter-Start liegen --> <!-- 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> <meta>
<!-- Zeitpunkt, an dem der Export erstellt wurde bzw. Datenstand --> <!-- Zeitpunkt, an dem der Export erstellt wurde bzw. Datenstand -->
<etl strategy="replace-visit" /> <etl strategy="replace-visit" />
...@@ -27,49 +29,50 @@ ...@@ -27,49 +29,50 @@
<!-- weitere metadaten n�tig? wertebereich, datentypen, ontologie, ...? --> <!-- weitere metadaten n�tig? wertebereich, datentypen, ontologie, ...? -->
</meta> </meta>
<visit> <patient id="XX12345">
<patid>XX12345</patid>
<surname>Dampf</surname> <surname>Dampf</surname>
<names>A</names> <names>A</names>
<birthdate>2001-01-01</birthdate> <birthdate>2001-01-01</birthdate>
<deathdate>2020</deathdate> <deathdate>2020</deathdate>
<sex>F</sex> <sex>F</sex>
<encounter start="2014-01-01T10:30:00" end="2014-01-05T10:30:00">XXE12345</encounter> <encounter id="XXE12345">
<location>Zuhause</location> <start>2014-01-01T10:30:00</start>
<!-- TODO inpatient/outpatient --> <end>2014-01-05T10:30:00</end>
<provider>xxxa</provider> <location>Zuhause</location>
<facts> <!-- TODO inpatient/outpatient -->
<fact concept="T:date:secs" start="2014-09-07T10:40:03"/> <provider>xxxa</provider>
<fact concept="T:date:mins" start="2014-09-07T10:40"/> <facts>
<fact concept="T:date:hours" start="2014-09-07T10"/> <fact concept="T:date:secs" start="2014-09-07T10:40:03"/>
<fact concept="T:date:day" start="2014-09-07"/> <fact concept="T:date:mins" start="2014-09-07T10:40"/>
<fact concept="T:date:month" start="2014-09"/> <fact concept="T:date:hours" start="2014-09-07T10"/>
<fact concept="T:date:year" start="2014"/> <fact concept="T:date:day" start="2014-09-07"/>
<fact concept="T:date:month" start="2014-09"/>
<!-- test parsing of data types --> <fact concept="T:date:year" start="2014"/>
<fact concept="T:type:str"><value xsi:type="string">abc123</value></fact>
<fact concept="T:type:int"><value xsi:type="numeric">123</value></fact>
<!-- value attributes can be used in elements fact and value -->
<fact concept="T:type:dec"><value xsi:type="numeric" unit="mm" flag="A">123.456</value></fact>
<fact concept="T:full" start="2010" end="2011" location="T:LOC"><value xsi:type="numeric" unit="mm" flag="A">123.456</value></fact>
<!--
<fact concept="T:type:enum" type="xsi:integer">1</fact>
-->
<!-- test group items -->
<fact concept="T:group:1">
<value xsi:type="string">groupvalue</value>
<modifier code="T:mod:1"/>
<modifier code="T:mod:2"><value xsi:type="string">def456</value></modifier>
<modifier code="T:mod:3"><value xsi:type="numeric" unit="mm" flag="A">78.9</value></modifier>
</fact>
<!-- group without value -->
<fact concept="T:group:2">
<modifier code="T:mod:1"/>
</fact>
</facts>
</visit>
<!-- weitere zeitstempel -->
</dwh-eav> <!-- test parsing of data types -->
<fact concept="T:type:str"><value xsi:type="string">abc123</value></fact>
<fact concept="T:type:int"><value xsi:type="numeric">123</value></fact>
<!-- value attributes can be used in elements fact and value -->
<fact concept="T:type:dec"><value xsi:type="numeric" unit="mm" flag="A">123.456</value></fact>
<fact concept="T:full" start="2010" end="2011" location="T:LOC"><value xsi:type="numeric" unit="mm" flag="A">123.456</value></fact>
<!--
<fact concept="T:type:enum" type="xsi:integer">1</fact>
-->
<!-- test group items -->
<fact concept="T:group:1">
<value xsi:type="string">groupvalue</value>
<modifier code="T:mod:1"/>
<modifier code="T:mod:2"><value xsi:type="string">def456</value></modifier>
<modifier code="T:mod:3"><value xsi:type="numeric" unit="mm" flag="A">78.9</value></modifier>
</fact>
<!-- group without value -->
<fact concept="T:group:2">
<modifier code="T:mod:1"/>
</fact>
</facts>
</encounter>
</patient>
</eav-data>
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
<property name="project">demo</property> <property name="project">demo</property>
<property name="nullProvider">LCS-I2B2:PROVIDERS</property> <property name="nullProvider">LCS-I2B2:PROVIDERS</property>
</plugin> </plugin>
<!-- supported file formats -->
<plugin class="de.sekmi.histream.io.XMLProviderFactory"/> <plugin class="de.sekmi.histream.io.XMLProviderFactory"/>
<plugin class="de.sekmi.histream.io.FlatProviderFactory"/> <plugin class="de.sekmi.histream.io.FlatProviderFactory"/>
......
...@@ -208,6 +208,7 @@ public class FlatObservationSupplier extends AbstractObservationParser implement ...@@ -208,6 +208,7 @@ public class FlatObservationSupplier extends AbstractObservationParser implement
} }
public FlatObservationSupplier(ObservationFactory factory, InputStream input) throws IOException{ public FlatObservationSupplier(ObservationFactory factory, InputStream input) throws IOException{
// TODO: standard API functionality: close() method will not close InputStream passed in constructor
this(factory, new BufferedReader(new InputStreamReader(input))); this(factory, new BufferedReader(new InputStreamReader(input)));
} }
......
package de.sekmi.histream.io; package de.sekmi.histream.io;
import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.UncheckedIOException;
import java.util.HashMap;
import java.util.Map;
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException; import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller; import javax.xml.bind.Unmarshaller;
import javax.xml.stream.FactoryConfigurationError; import javax.xml.stream.FactoryConfigurationError;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import de.sekmi.histream.DateTimeAccuracy;
import de.sekmi.histream.Observation; import de.sekmi.histream.Observation;
import de.sekmi.histream.ObservationFactory; import de.sekmi.histream.ObservationFactory;
import de.sekmi.histream.ObservationSupplier;
import de.sekmi.histream.impl.ObservationImpl; import de.sekmi.histream.impl.ObservationImpl;
public class JAXBObservationSupplier extends XMLObservationSupplier { public class JAXBObservationSupplier implements ObservationSupplier {
private static final String DOCUMENT_ROOT = "eav-data";
private static final String PATIENT_ELEMENT = "patient";
private static final String ENCOUNTER_ELEMENT = "encounter";
private static final String FACT_WRAPPER = "facts";
private ObservationFactory factory;
private Unmarshaller unmarshaller; private Unmarshaller unmarshaller;
private XMLStreamReader reader;
private Map<String,String> meta;
// patient data
private String patientId;
private Map<String,String> patientData;
// encounter data
private String encounterId;
private DateTimeAccuracy encounterStart;
private DateTimeAccuracy encounterEnd;
private Map<String,String> visitData;
public JAXBObservationSupplier(ObservationFactory factory, InputStream input)
throws XMLStreamException, FactoryConfigurationError, JAXBException { public JAXBObservationSupplier(ObservationFactory factory, InputStream input)throws JAXBException, XMLStreamException, FactoryConfigurationError{
super(factory, input); this(factory, XMLInputFactory.newInstance().createXMLStreamReader(input));
}
public JAXBObservationSupplier(ObservationFactory factory, XMLStreamReader reader) throws JAXBException, XMLStreamException{
this.factory = factory;
this.meta = new HashMap<>();
this.patientData = new HashMap<>();
this.visitData = new HashMap<>();
unmarshaller = JAXBContext.newInstance(ObservationImpl.class).createUnmarshaller(); unmarshaller = JAXBContext.newInstance(ObservationImpl.class).createUnmarshaller();
// TODO: set schema
//unmarshaller.setSchema(schema);
this.reader = reader;
readToRoot();
readMeta();
readPatient();
readEncounter();
}
private void readToRoot() throws XMLStreamException{
while( reader.hasNext() ){
reader.next();
if( reader.isStartElement() )break;
}
if( !reader.isStartElement() || !reader.getLocalName().equals(DOCUMENT_ROOT) ){
throw new XMLStreamException("Start element '"+DOCUMENT_ROOT+"' expected instead of "+reader.getLocalName(), reader.getLocation());
}
reader.nextTag();
}
private void readMeta()throws XMLStreamException{
if( !reader.isStartElement() || !reader.getLocalName().equals("meta") )return;
// read meta
reader.nextTag();
if( reader.getLocalName().equals("etl") ){
String etlStrategy = reader.getAttributeValue(null, "strategy");
// TODO use constants for etl.strategy, etc.
if( etlStrategy != null )meta.put("etl.strategy", etlStrategy);
reader.nextTag();
// should be end element
reader.nextTag();
}
if( reader.getLocalName().equals("source") ){
meta.put("source.timestamp", reader.getAttributeValue(null, "timestamp"));
meta.put("source.id", reader.getAttributeValue(null, "id"));
reader.nextTag();
// should be end element
reader.nextTag();
}
// skip to end of meta
while( !reader.isEndElement() || !reader.getLocalName().equals("meta") ){
reader.next();
}
reader.nextTag();
} }
@Override
/**
* Reads patient element with content. Precondition patient start element. Postcondition encounter start element.
* @throws XMLStreamException on error
*/
private void readPatient() throws XMLStreamException{
if( !reader.getLocalName().equals(PATIENT_ELEMENT) ){
throw new XMLStreamException("Expected element: "+PATIENT_ELEMENT,reader.getLocation());
}
patientId = reader.getAttributeValue(null, "id");
patientData.clear();
reader.nextTag();
while( reader.isStartElement()
&& !reader.getLocalName().equals(ENCOUNTER_ELEMENT) )
{
patientData.put(reader.getLocalName(), reader.getElementText());
reader.nextTag();
}
}
/**
* Reads encounter element with content.
* Precondition encounter start element.
* Postcondition facts start element.
* @throws XMLStreamException on error
*/
private void readEncounter() throws XMLStreamException{
if( !reader.getLocalName().equals(ENCOUNTER_ELEMENT) ){
throw new XMLStreamException("Expected element: "+ENCOUNTER_ELEMENT,reader.getLocation());
}
visitData.clear();
encounterId = reader.getAttributeValue(null, "id");
reader.nextTag();
while( reader.isStartElement()
&& !reader.getLocalName().equals(FACT_WRAPPER) )
{
visitData.put(reader.getLocalName(), reader.getElementText());
reader.nextTag();
}
if( visitData.containsKey("start") ){
encounterStart = DateTimeAccuracy.parsePartialIso8601(visitData.get("start"));
}
if( visitData.containsKey("end") ){
encounterEnd = DateTimeAccuracy.parsePartialIso8601(visitData.get("end"));
}
// TODO assert at <facts>
reader.nextTag();
}
protected Observation readObservation()throws XMLStreamException{ protected Observation readObservation()throws XMLStreamException{
if( reader.isWhiteSpace() ){ if( reader.isWhiteSpace() ){
reader.nextTag(); reader.nextTag();
...@@ -35,42 +157,79 @@ public class JAXBObservationSupplier extends XMLObservationSupplier { ...@@ -35,42 +157,79 @@ public class JAXBObservationSupplier extends XMLObservationSupplier {
// </facts> might occur after previous call to readObservation() // </facts> might occur after previous call to readObservation()
while( reader.isEndElement() ){ while( reader.isEndElement() ){
switch( reader.getLocalName() ){ switch( reader.getLocalName() ){
case "facts": case FACT_WRAPPER:
// end of facts // end of facts
reader.nextTag(); reader.nextTag();
case "visit": // fall through to end of visit,
// XXX this doesn't work, if facts are allowed outside of encounter (e.g. directly under patient)
case ENCOUNTER_ELEMENT:
// end of visit // end of visit
reader.nextTag(); reader.nextTag();
if( reader.isStartElement() && reader.getLocalName().equals("visit") ){ if( reader.isStartElement() && reader.getLocalName().equals(ENCOUNTER_ELEMENT) ){
// next visit // next visit
readVisit(); readEncounter();
}
break;
case PATIENT_ELEMENT:
// end of patient
reader.nextTag();
if( reader.isStartElement() && reader.getLocalName().equals(PATIENT_ELEMENT) ){
readPatient();
readEncounter();
} }
break; break;
case "dwh-eav": case DOCUMENT_ROOT:
// end of document // end of document
return null; return null;
} }
} }
// start element of eav-item or eav-group // start element of eav-item or eav-group
if( !reader.isStartElement() if( !reader.isStartElement() ){
|| !(reader.getLocalName().equals("fact")) ){ throw new XMLStreamException("Element patient, encounter or fact expected instead of "+reader.getLocalName(), reader.getLocation());
throw new XMLStreamException("Element fact expected instead of "+reader.getLocalName(), reader.getLocation()); }
if( reader.getLocalName().equals(PATIENT_ELEMENT) ){
} }
ObservationImpl fact; ObservationImpl fact;
try { try {
fact = (ObservationImpl)unmarshaller.unmarshal(reader); fact = (ObservationImpl)unmarshaller.unmarshal(reader);
// TODO: set factory
} catch (JAXBException e) { } catch (JAXBException e) {
throw new XMLStreamException( e); throw new XMLStreamException( e);
} }
if( fact.getPatientId() == null ){ if( fact.getPatientId() != null && !fact.getPatientId().equals(patientId) ){
fact.setPatientId(visitData.get("patid")); throw new XMLStreamException("Fact patid differs from patient id", reader.getLocation());
} }
fact.setPatientId(patientId);
fact.setEncounterId(encounterId);
if( fact.getStartTime() == null ){ if( fact.getStartTime() == null ){
fact.setStartTime(encounterStart); fact.setStartTime(encounterStart);
fact.setEndTime(encounterEnd);
} }
// TODO set etc. from visit // TODO set etc. from visit
// TODO set ObservationFactory, initialize extensions // TODO set ObservationFactory, initialize extensions
return fact; return fact;
} }
@Override
public Observation get() {
try {
return readObservation();
} catch (XMLStreamException e) {
throw new UncheckedIOException(new IOException(e));
}
}
@Override
public String getMeta(String key) {
return meta.get(key);
}
@Override
public void close() throws XMLStreamException {
reader.close();
}
} }
...@@ -199,7 +199,7 @@ public class FileObservationProviderTest { ...@@ -199,7 +199,7 @@ public class FileObservationProviderTest {
@Test @Test
public void testJAXBReader() throws FileNotFoundException, XMLStreamException, FactoryConfigurationError, JAXBException { public void testJAXBReader() throws FileNotFoundException, XMLStreamException, FactoryConfigurationError, JAXBException {
XMLObservationSupplier xos = new JAXBObservationSupplier(factory, new FileInputStream("examples/dwh-jaxb.xml")); JAXBObservationSupplier xos = new JAXBObservationSupplier(factory, new FileInputStream("examples/dwh-jaxb.xml"));
validateExample(xos); validateExample(xos);
xos.close(); xos.close();
} }
......
...@@ -8,8 +8,6 @@ import org.junit.Test; ...@@ -8,8 +8,6 @@ import org.junit.Test;
public class TransformerTest { public class TransformerTest {
@SuppressWarnings("resource")
@Test @Test
public void testPullTransformerIdentity() throws FileNotFoundException, IOException{ public void testPullTransformerIdentity() throws FileNotFoundException, IOException{
FileObservationProviderTest f = new FileObservationProviderTest(); FileObservationProviderTest f = new FileObservationProviderTest();
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment