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

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;
import java.io.IOException;
import java.util.function.Consumer;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.stream.XMLStreamException;
......@@ -11,6 +12,7 @@ import org.w3c.dom.Element;
import de.sekmi.histream.ObservationException;
import de.sekmi.histream.export.config.Concept;
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.ExportException;
......@@ -26,19 +28,34 @@ import de.sekmi.histream.export.config.ExportException;
*/
class FragmentExporter extends VisitFragmentParser {
TableParser patientParser;
TableParser visitParser;
private TableParser patientParser;
private TableParser visitParser;
private EavTableParser[] eavParsers;
private Element currentPatient;
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 {
super();
try {
patientParser = desc.getPatientTable().createParser(writer.openPatientTable(), xpath);
visitParser = desc.getVisitTable().createParser(writer.openVisitTable(), xpath);
// open eav parsers
openEavTables(desc.getEAVTables(), writer, xpath);
} catch (IOException e) {
close( e::addSuppressed );
throw new ExportException("Unable to open table for writing", e);
} catch( ExportException e ){
close( e::addSuppressed );
throw e;
}
// initialise annotator
factAnnotator = new FactClassAnnotator();
......@@ -68,12 +85,17 @@ class FragmentExporter extends VisitFragmentParser {
protected void patientFragment(Element patient) throws ObservationException {
currentPatient = patient;
try {
patientParser.writeRow(patient);
patientParser.processNode(patient);
} catch (ExportException | IOException 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
protected void visitFragment(Element visit) throws ObservationException {
// annotate facts with class attribute
......@@ -83,7 +105,8 @@ class FragmentExporter extends VisitFragmentParser {
// the parent element. E.g. '../@id' to get the patient id
currentPatient.appendChild(visit);
try {
visitParser.writeRow(visit);
visitParser.processNode(visit);
writeEavFacts(visit);
} catch (ExportException | IOException e) {
throw new ObservationException(e);
} finally {
......@@ -91,20 +114,40 @@ class FragmentExporter extends VisitFragmentParser {
currentPatient.removeChild(visit);
}
// TODO check for repeating concepts and write to separate parsers
// iterate through facts and
}
@Override
public void close(){
super.close();
try{
patientParser.close();
}catch( IOException e ){
reportError(new ObservationException(e));
close( e -> reportError(new ObservationException(e)) );
}
private void close(Consumer<IOException> errorAction){
if( patientParser != null ){
try{
patientParser.close();
}catch( IOException e ){
errorAction.accept(e);
}
}
try{
visitParser.close();
}catch( IOException e ){
reportError(new ObservationException(e));
if( visitParser != null ){
try{
visitParser.close();
}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{
writeHeaders();
}
protected AbstractTable getTable(){
return table;
}
private void compileXPaths() throws ExportException{
Column[] columns = table.getColumns();
xpaths = new XPathExpression[columns.length];
......@@ -51,9 +54,10 @@ public class TableParser implements AutoCloseable{
writer.header(table.getHeaders());
}
public void writeRow(Node node) throws ExportException, IOException{
public void processNode(Node node) throws ExportException, IOException{
writer.row(valuesForFragment(node));
}
private String[] valuesForFragment(Node node) throws ExportException{
Column[] columns = table.getColumns();
String[] values = new String[columns.length];
......
package de.sekmi.histream.export.config;
import java.io.IOException;
import javax.xml.bind.annotation.XmlAttribute;
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{
......@@ -10,12 +15,20 @@ public class EavTable extends AbstractTable{
@XmlAttribute
String id;
@XmlIDREF
@XmlAttribute(name="class")
ConceptGroup clazz;
@XmlAttribute
String xpath;
@Override
public String getId() {
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{
}
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
public void close(){
}
......
......@@ -35,8 +35,12 @@ public class TestExport {
export.export(s, m);
}
// 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();
// 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
}
......
......@@ -3,7 +3,11 @@
xmlns:eav="http://sekmi.de/histream/ns/eav-data">
<concepts>
<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>
<concept notation="T:full"/>
<concept wildcard-notation="CEDIS:*"/>
......@@ -24,4 +28,12 @@
<column header="f_loc" xpath="eav:fact[@concept='T:full']/@location"/>
<column header="f_val" xpath="eav:fact[@concept='T:full']/eav:value"/>
</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>
Markdown is supported
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