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

eav table export implemented

parent 5039a9dd
package de.sekmi.histream.export;
import java.io.IOException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import de.sekmi.histream.export.config.EavTable;
import de.sekmi.histream.export.config.ExportException;
public class EavTableParser extends TableParser {
private XPathExpression factSelector;
public EavTableParser(EavTable table, TableWriter writer, XPath xpath) throws ExportException, IOException {
super(table, writer, xpath);
try {
factSelector = xpath.compile(table.getXPath());
} catch (XPathExpressionException e) {
throw new ExportException("Unable to compile xpath attribute '"+table.getXPath()+"' for table "+table.getId());
}
}
@Override
public void processNode(Node visit) throws ExportException, IOException {
// select facts with xpath expression on visit element
NodeList nl;
try {
nl = (NodeList)factSelector.evaluate(visit, XPathConstants.NODESET);
} catch (XPathExpressionException e) {
throw new ExportException("XPath evaluation failed for eav-table[@id='"+getTable().getId()+"']/@xpath", e);
}
for( int i=0; i< nl.getLength(); i++ ){
Node n = nl.item(i);
// make sure only fact elements are selected
if( n.getNodeType() != Node.ELEMENT_NODE || !n.getLocalName().equals("fact") ){
throw new ExportException("xpath for eav table '"+getTable().getId()+" must select only 'fact' elements. instead found "+n.toString());
}
// write table row for each selected fact
super.processNode(n);
}
}
}
package de.sekmi.histream.export; package de.sekmi.histream.export;
import java.io.IOException; import java.io.IOException;
import java.util.function.Consumer;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamException;
...@@ -11,6 +12,7 @@ import org.w3c.dom.Element; ...@@ -11,6 +12,7 @@ import org.w3c.dom.Element;
import de.sekmi.histream.ObservationException; import de.sekmi.histream.ObservationException;
import de.sekmi.histream.export.config.Concept; import de.sekmi.histream.export.config.Concept;
import de.sekmi.histream.export.config.ConceptGroup; import de.sekmi.histream.export.config.ConceptGroup;
import de.sekmi.histream.export.config.EavTable;
import de.sekmi.histream.export.config.ExportDescriptor; import de.sekmi.histream.export.config.ExportDescriptor;
import de.sekmi.histream.export.config.ExportException; import de.sekmi.histream.export.config.ExportException;
...@@ -26,19 +28,34 @@ import de.sekmi.histream.export.config.ExportException; ...@@ -26,19 +28,34 @@ import de.sekmi.histream.export.config.ExportException;
*/ */
class FragmentExporter extends VisitFragmentParser { class FragmentExporter extends VisitFragmentParser {
TableParser patientParser; private TableParser patientParser;
TableParser visitParser; private TableParser visitParser;
private EavTableParser[] eavParsers;
private Element currentPatient; private Element currentPatient;
private FactClassAnnotator factAnnotator; private FactClassAnnotator factAnnotator;
private void openEavTables(EavTable[] tables, ExportWriter writer, XPath xpath) throws ExportException, IOException{
eavParsers = new EavTableParser[tables.length];
for( int i=0; i<eavParsers.length; i++ ){
eavParsers[i] = tables[i].createParser(writer.openEAVTable(tables[i].getId()), xpath);
}
}
protected FragmentExporter(XPath xpath, ExportDescriptor desc, ExportWriter writer) throws ExportException, XMLStreamException, ParserConfigurationException { protected FragmentExporter(XPath xpath, ExportDescriptor desc, ExportWriter writer) throws ExportException, XMLStreamException, ParserConfigurationException {
super(); super();
try { try {
patientParser = desc.getPatientTable().createParser(writer.openPatientTable(), xpath); patientParser = desc.getPatientTable().createParser(writer.openPatientTable(), xpath);
visitParser = desc.getVisitTable().createParser(writer.openVisitTable(), xpath); visitParser = desc.getVisitTable().createParser(writer.openVisitTable(), xpath);
// open eav parsers
openEavTables(desc.getEAVTables(), writer, xpath);
} catch (IOException e) { } catch (IOException e) {
close( e::addSuppressed );
throw new ExportException("Unable to open table for writing", e); throw new ExportException("Unable to open table for writing", e);
} catch( ExportException e ){
close( e::addSuppressed );
throw e;
} }
// initialise annotator // initialise annotator
factAnnotator = new FactClassAnnotator(); factAnnotator = new FactClassAnnotator();
...@@ -68,12 +85,17 @@ class FragmentExporter extends VisitFragmentParser { ...@@ -68,12 +85,17 @@ class FragmentExporter extends VisitFragmentParser {
protected void patientFragment(Element patient) throws ObservationException { protected void patientFragment(Element patient) throws ObservationException {
currentPatient = patient; currentPatient = patient;
try { try {
patientParser.writeRow(patient); patientParser.processNode(patient);
} catch (ExportException | IOException e) { } catch (ExportException | IOException e) {
throw new ObservationException(e); throw new ObservationException(e);
} }
} }
private void writeEavFacts(Element visit) throws ExportException, IOException{
for( int i=0; i<eavParsers.length; i++ ){
eavParsers[i].processNode(visit);
}
}
@Override @Override
protected void visitFragment(Element visit) throws ObservationException { protected void visitFragment(Element visit) throws ObservationException {
// annotate facts with class attribute // annotate facts with class attribute
...@@ -83,7 +105,8 @@ class FragmentExporter extends VisitFragmentParser { ...@@ -83,7 +105,8 @@ class FragmentExporter extends VisitFragmentParser {
// the parent element. E.g. '../@id' to get the patient id // the parent element. E.g. '../@id' to get the patient id
currentPatient.appendChild(visit); currentPatient.appendChild(visit);
try { try {
visitParser.writeRow(visit); visitParser.processNode(visit);
writeEavFacts(visit);
} catch (ExportException | IOException e) { } catch (ExportException | IOException e) {
throw new ObservationException(e); throw new ObservationException(e);
} finally { } finally {
...@@ -91,20 +114,40 @@ class FragmentExporter extends VisitFragmentParser { ...@@ -91,20 +114,40 @@ class FragmentExporter extends VisitFragmentParser {
currentPatient.removeChild(visit); currentPatient.removeChild(visit);
} }
// TODO check for repeating concepts and write to separate parsers // TODO check for repeating concepts and write to separate parsers
// iterate through facts and
} }
@Override @Override
public void close(){ public void close(){
super.close(); super.close();
try{ close( e -> reportError(new ObservationException(e)) );
patientParser.close(); }
}catch( IOException e ){ private void close(Consumer<IOException> errorAction){
reportError(new ObservationException(e)); if( patientParser != null ){
try{
patientParser.close();
}catch( IOException e ){
errorAction.accept(e);
}
} }
try{ if( visitParser != null ){
visitParser.close(); try{
}catch( IOException e ){ visitParser.close();
reportError(new ObservationException(e)); }catch( IOException e ){
errorAction.accept(e);
}
}
if( eavParsers != null ){
for( int i=0; i<eavParsers.length; i++ ){
if( eavParsers[i] == null ){
continue;
}
try{
eavParsers[i].close();
}catch( IOException e ){
errorAction.accept(e);
}
}
} }
} }
} }
...@@ -35,6 +35,9 @@ public class TableParser implements AutoCloseable{ ...@@ -35,6 +35,9 @@ public class TableParser implements AutoCloseable{
writeHeaders(); writeHeaders();
} }
protected AbstractTable getTable(){
return table;
}
private void compileXPaths() throws ExportException{ private void compileXPaths() throws ExportException{
Column[] columns = table.getColumns(); Column[] columns = table.getColumns();
xpaths = new XPathExpression[columns.length]; xpaths = new XPathExpression[columns.length];
...@@ -51,9 +54,10 @@ public class TableParser implements AutoCloseable{ ...@@ -51,9 +54,10 @@ public class TableParser implements AutoCloseable{
writer.header(table.getHeaders()); writer.header(table.getHeaders());
} }
public void writeRow(Node node) throws ExportException, IOException{ public void processNode(Node node) throws ExportException, IOException{
writer.row(valuesForFragment(node)); writer.row(valuesForFragment(node));
} }
private String[] valuesForFragment(Node node) throws ExportException{ private String[] valuesForFragment(Node node) throws ExportException{
Column[] columns = table.getColumns(); Column[] columns = table.getColumns();
String[] values = new String[columns.length]; String[] values = new String[columns.length];
......
package de.sekmi.histream.export.config; package de.sekmi.histream.export.config;
import java.io.IOException;
import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlID; import javax.xml.bind.annotation.XmlID;
import javax.xml.bind.annotation.XmlIDREF; import javax.xml.xpath.XPath;
import de.sekmi.histream.export.EavTableParser;
import de.sekmi.histream.export.TableWriter;
public class EavTable extends AbstractTable{ public class EavTable extends AbstractTable{
...@@ -10,12 +15,20 @@ public class EavTable extends AbstractTable{ ...@@ -10,12 +15,20 @@ public class EavTable extends AbstractTable{
@XmlAttribute @XmlAttribute
String id; String id;
@XmlIDREF @XmlAttribute
@XmlAttribute(name="class") String xpath;
ConceptGroup clazz;
@Override @Override
public String getId() { public String getId() {
return id; return id;
} }
public String getXPath(){
return xpath;
}
@Override
public EavTableParser createParser(TableWriter writer, XPath xpath) throws ExportException, IOException {
return new EavTableParser(this, writer, xpath);
}
} }
...@@ -118,6 +118,13 @@ public class MemoryExportWriter implements ExportWriter{ ...@@ -118,6 +118,13 @@ public class MemoryExportWriter implements ExportWriter{
} }
return t.rows.get(row)[index]; return t.rows.get(row)[index];
} }
public int rowCount(String table) throws IndexOutOfBoundsException{
MemoryTable t = tables.get(table);
if( t == null ){
throw new IndexOutOfBoundsException("Non-existing table "+table);
}
return t.rows.size();
}
@Override @Override
public void close(){ public void close(){
} }
......
...@@ -35,8 +35,12 @@ public class TestExport { ...@@ -35,8 +35,12 @@ public class TestExport {
export.export(s, m); export.export(s, m);
} }
// verify class lookup // verify class lookup
Assert.assertEquals("T:date:year", m.get(MemoryExportWriter.VISIT_TABLE, "byclass", 0)); Assert.assertEquals("T:type:str", m.get(MemoryExportWriter.VISIT_TABLE, "byclass", 0));
m.dump(); m.dump();
// verify eav table
Assert.assertEquals(6, m.rowCount("eavtabletest"));
Assert.assertEquals("T:date:month", m.get("eavtabletest", "code", 4));
// TODO something wrong with namespaces in xpath/dom // TODO something wrong with namespaces in xpath/dom
} }
......
...@@ -3,7 +3,11 @@ ...@@ -3,7 +3,11 @@
xmlns:eav="http://sekmi.de/histream/ns/eav-data"> xmlns:eav="http://sekmi.de/histream/ns/eav-data">
<concepts> <concepts>
<group class="testclass"> <group class="testclass">
<concept wildcard-notation="T:date:yea*"/> <concept wildcard-notation="T:type:st*"/>
</group>
<group class="testeav">
<!-- will cause problems if wildcards are overlapping -->
<concept wildcard-notation="T:date:*"/>
</group> </group>
<concept notation="T:full"/> <concept notation="T:full"/>
<concept wildcard-notation="CEDIS:*"/> <concept wildcard-notation="CEDIS:*"/>
...@@ -24,4 +28,12 @@ ...@@ -24,4 +28,12 @@
<column header="f_loc" xpath="eav:fact[@concept='T:full']/@location"/> <column header="f_loc" xpath="eav:fact[@concept='T:full']/@location"/>
<column header="f_val" xpath="eav:fact[@concept='T:full']/eav:value"/> <column header="f_val" xpath="eav:fact[@concept='T:full']/eav:value"/>
</visit-table> </visit-table>
<eav-table id="eavtabletest" xpath="eav:fact[@class='testeav']">
<!-- context for XPath expressions is each fact node -->
<column header="pid" xpath="../../@id"/>
<column header="visit" xpath="../@id"/>
<column header="code" xpath="@concept"/>
<column header="start" xpath="@start"/>
</eav-table>
</export> </export>
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