Commit 6a92cdf5 authored by R.W.Majeed's avatar R.W.Majeed

Cross check column headers with configuration for patient table

parent 9241b742
......@@ -7,7 +7,7 @@ import java.util.Objects;
import de.sekmi.histream.etl.config.Column;
/**
* Maps {@link Column}s to header/table indices
* Maps {@link Column}s to header/table indices.
*
* @author Raphael
*
......@@ -20,6 +20,11 @@ public class ColumnMap{
*/
Map<String, Integer> map;
/**
* Construct a column map with ordered list of headers
*
* @param headers headers
*/
public ColumnMap(String[] headers){
this.headers = headers;
this.map = new HashMap<>();
......@@ -27,6 +32,8 @@ public class ColumnMap{
/**
* Register a column and lookup it's index in the header list.
* The index is stored for later retrieval via {@link #indexOf(Column)}.
*
* @param column column to register
* @throws ParseException if the column cannot be found in the headers
* @throws NullPointerException if column is null
......@@ -54,7 +61,31 @@ public class ColumnMap{
}
throw new ParseException("Column name '"+column.getName()+"' not found in header");
}
/**
* Get header index of the specified column.
*
* @param column column
* @return index in header list (specified in constructor)
*/
public Integer indexOf(Column<?> column){
return map.get(column.getName());
}
/**
* Determine whether the specified header has a column associated
*
* @param header header
* @return true if a column was registered, false otherwise
*/
public boolean isRegistered(String header){
return map.containsKey(header);
}
/**
* Get the number of registered columns
* @return column count
*/
public int size(){
return map.size();
}
}
\ No newline at end of file
......@@ -79,10 +79,11 @@ public class ETLObservationSupplier implements ObservationSupplier{
* @param factory observation factory
* @return observation supplier
*
* @throws IOException error reading configuration. The error might be caused by a {@link ParseException}.
* @throws IOException error reading configuration or data tables.
* @throws ParseException configuration error
*
*/
public static ETLObservationSupplier load(URL configuration, ObservationFactory factory) throws IOException{
public static ETLObservationSupplier load(URL configuration, ObservationFactory factory) throws IOException, ParseException{
DataSource ds = JAXB.unmarshal(configuration, DataSource.class);
ds.getMeta().setLocation(configuration);
return new ETLObservationSupplier(ds, factory);
......@@ -94,9 +95,10 @@ public class ETLObservationSupplier implements ObservationSupplier{
*
* @param configuration configuration URL
* @return observation factory
* @throws IOException error reading configuration
* @throws IOException error reading configuration or table data
* @throws ParseException configuration error
*/
public static ETLObservationSupplier load(URL configuration) throws IOException{
public static ETLObservationSupplier load(URL configuration) throws IOException, ParseException{
ObservationFactory of = new ObservationFactoryImpl();
of.registerExtension(new SimplePatientExtension());
of.registerExtension(new SimpleVisitExtension());
......@@ -107,9 +109,10 @@ public class ETLObservationSupplier implements ObservationSupplier{
*
* @param ds data source
* @param factory observation factory
* @throws IOException error reading configuration
* @throws IOException error reading configuration or table data
* @throws ParseException configuration error
*/
public ETLObservationSupplier(DataSource ds, ObservationFactory factory) throws IOException {
public ETLObservationSupplier(DataSource ds, ObservationFactory factory) throws IOException, ParseException {
this.ds = ds;
pt = ds.getPatientTable();
......@@ -119,7 +122,7 @@ public class ETLObservationSupplier implements ObservationSupplier{
Meta meta = ds.getMeta();
// in case of exception, make sure already opened suppliers are closed
IOException error = null;
Exception error = null;
try{
pr = pt.open(factory, meta);
vr = vt.open(factory, meta);
......@@ -130,7 +133,7 @@ public class ETLObservationSupplier implements ObservationSupplier{
// open all tables
wr = new ArrayList<>(wt.size());
for( WideTable t : wt ){
@SuppressWarnings("resource")
//@SuppressWarnings("resource")
RecordSupplier<WideRow> s = t.open(factory, meta);
queue.addFactTable(s);
wr.add(s);
......@@ -140,9 +143,9 @@ public class ETLObservationSupplier implements ObservationSupplier{
}catch( UncheckedIOException e ){
error = e.getCause();
}catch( UncheckedParseException e ){
error = new IOException(e.getCause());
error = e.getCause();
}catch( ParseException e ){
error = new IOException(e);
error = e;
}catch( IOException e ){
error = e;
}
......@@ -152,7 +155,11 @@ public class ETLObservationSupplier implements ObservationSupplier{
}catch( IOException f ){
error.addSuppressed(f);
}
throw error;
if( error instanceof ParseException ){
throw (ParseException)error;
}else{
throw (IOException)error;
}
}
}
......
package de.sekmi.histream.etl.config;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
......@@ -42,12 +41,30 @@ public class PatientTable extends Table<PatientRow> implements ConceptTable{
public ColumnMap getColumnMap(String[] headers) throws ParseException {
ColumnMap map = new ColumnMap(headers);
// need patientId at minimum
if( idat.patientId == null ){
throw new ParseException("datasource/patient-table/idat/patient-id column not specified");
}
map.registerColumn(idat.patientId);
map.registerColumn(idat.givenName);
map.registerColumn(idat.surname);
map.registerColumn(idat.birthdate);
map.registerColumn(idat.deathdate);
map.registerColumn(idat.gender);
// other columns are optional
if( idat.givenName != null ){
map.registerColumn(idat.givenName);
}
if( idat.surname != null ){
map.registerColumn(idat.surname);
}
if( idat.birthdate != null ){
map.registerColumn(idat.birthdate);
}
if( idat.deathdate != null ){
map.registerColumn(idat.deathdate);
}
if( idat.gender != null ){
map.registerColumn(idat.gender);
}
// make sure all headers are specified in configuration
Table.validateAllHeaders(headers, map, idat.ignore);
return map;
}
......
......@@ -52,6 +52,33 @@ public abstract class Table<T extends FactRow> {
}
}
/**
* Make sure all columns are either registered in the column map or specified in an 'ignore' element.
* Otherwise, a {@link ParseException} is thrown.
*
* @param headers table headers
* @param map column map
* @param ignored ignored columns
* @throws ParseException thrown for the first header which is neither in the map nor in ignored columns
*/
protected static void validateAllHeaders(String[] headers, ColumnMap map, Column<?>[] ignored) throws ParseException{
// for each header
for( int i=0; i<headers.length; i++ ){
// check if in map
if( map.isRegistered(headers[i]) )continue;
// not registered in map
// check if listed in ignore element
int j=0;
for( j=0; j<ignored.length; j++ ){
if( headers[i].equals(ignored[j].getName()) )break;
}
if( j == ignored.length ){
// unassigned column
throw new ParseException("Header not specified in configuration: "+headers[i]);
}
}
}
public abstract T fillRecord(ColumnMap map, Object[] row, ObservationFactory factory) throws ParseException;
public RecordSupplier<T> open(ObservationFactory factory, Meta meta) throws IOException, ParseException{
......
patid inogriert1 nachname vorname geburtsdatum verstorben geschlecht patfakt1
patid ignoriert1 nachname vorname geburtsdatum verstorben geschlecht patfakt1
p1 a n1 v1 01.02.2003 11.02.2003 F c
p2 b n2 v2 02.03.2004 M d
p3 c n3 v3 03.04.2005 F e
\ No newline at end of file
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