1
package org.hisp.dhis.integration.rims.action;
3
import java.sql.SQLException;
4
import java.util.ArrayList;
5
import java.util.Calendar;
6
import java.util.Collection;
8
import java.util.Iterator;
10
import java.util.regex.Pattern;
12
import org.hibernate.HibernateException;
13
import org.hisp.dhis.aggregation.batch.statement.StatementManager;
14
import org.hisp.dhis.dataelement.DataElement;
15
import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
16
import org.hisp.dhis.dataelement.DataElementCategoryOptionComboService;
17
import org.hisp.dhis.dataelement.DataElementService;
18
import org.hisp.dhis.dataset.DataSet;
19
import org.hisp.dhis.dataset.DataSetService;
20
import org.hisp.dhis.datavalue.DataValue;
21
import org.hisp.dhis.datavalue.DataValueService;
22
import org.hisp.dhis.i18n.I18nFormat;
23
import org.hisp.dhis.integration.rims.api.RIMSService;
24
import org.hisp.dhis.integration.rims.api.RIMSTable;
25
import org.hisp.dhis.integration.rims.api.RIMS_Mapping_DataElement;
26
import org.hisp.dhis.integration.rims.api.RIMS_Mapping_Orgunit;
27
import org.hisp.dhis.integration.rims.api.tables.DistrictTable;
28
import org.hisp.dhis.integration.rims.api.tables.PHCTable;
29
import org.hisp.dhis.integration.rims.api.tables.RIMSDistrict;
30
import org.hisp.dhis.integration.rims.api.tables.RIMSOrgUnit;
31
import org.hisp.dhis.integration.rims.api.tables.RIMS_PHC;
32
import org.hisp.dhis.integration.rims.util.Configuration;
33
import org.hisp.dhis.integration.rims.util.ImportExportError;
34
import org.hisp.dhis.organisationunit.OrganisationUnit;
35
import org.hisp.dhis.organisationunit.OrganisationUnitService;
36
import org.hisp.dhis.period.MonthlyPeriodType;
37
import org.hisp.dhis.period.Period;
38
import org.hisp.dhis.period.PeriodService;
39
import org.hisp.dhis.period.PeriodType;
40
import org.hisp.dhis.period.YearlyPeriodType;
42
import com.opensymphony.xwork.ActionSupport;
44
import static java.util.Calendar.YEAR;
45
import static java.util.Calendar.MONTH;
46
import static java.util.Calendar.DAY_OF_MONTH;
47
import static java.util.Calendar.DAY_OF_YEAR;
49
public class RIMSImportResultAction
52
// -------------------------------------------------------------------------
54
// -------------------------------------------------------------------------
55
private StatementManager statementManager;
57
public void setStatementManager( StatementManager statementManager )
59
this.statementManager = statementManager;
62
private RIMSService rimsService;
64
public void setRimsService( RIMSService rimsService )
66
this.rimsService = rimsService;
69
private DataSetService dataSetService;
71
public void setDataSetService( DataSetService dataSetService )
73
this.dataSetService = dataSetService;
76
private PeriodService periodService;
78
public void setPeriodService( PeriodService periodService )
80
this.periodService = periodService;
83
private OrganisationUnitService organisationUnitService;
85
public void setOrganisationUnitService( OrganisationUnitService organisationUnitService )
87
this.organisationUnitService = organisationUnitService;
90
private DataElementService dataElementService;
92
public void setDataElementService( DataElementService dataElementService )
94
this.dataElementService = dataElementService;
97
private DataValueService dataValueService;
99
public void setDataValueService( DataValueService dataValueService )
101
this.dataValueService = dataValueService;
104
private DataElementCategoryOptionComboService dataElementCategoryOptionComboService;
106
public void setDataElementCategoryOptionComboService(
107
DataElementCategoryOptionComboService dataElementCategoryOptionComboService )
109
this.dataElementCategoryOptionComboService = dataElementCategoryOptionComboService;
112
private I18nFormat format;
114
public void setFormat( I18nFormat format )
116
this.format = format;
119
// --------------------------------------------------------------------------
121
// --------------------------------------------------------------------------
127
private List<String> selectedOrgunits;
129
public void setSelectedOrgunits( List<String> selectedOrgunits )
131
this.selectedOrgunits = selectedOrgunits;
134
private List<String> rimsDEGroups;
136
public void setRimsDEGroups( List<String> rimsDEGroups )
138
this.rimsDEGroups = rimsDEGroups;
141
private String startDate;
143
public void setStartDate( String startDate )
145
this.startDate = startDate;
148
private String endDate;
150
public void setEndDate( String endDate )
152
this.endDate = endDate;
155
private String rimsDistricts;
157
private String includeDistrict;
159
private String connection;
161
private int totalRecCount;
163
private Collection<ImportExportError> skipped = new ArrayList<ImportExportError>();
165
private boolean abort;
167
public int getTotalRecCount()
169
return totalRecCount;
172
// --------------------------------------------------------------------------
173
// Action Implementation
174
// --------------------------------------------------------------------------
175
public String execute()
178
// TODO Useful for testing, turn off for production use
179
boolean includeTotals = false;
181
Configuration config = new Configuration( connection );
182
statementManager.initialise();
184
// ---------------------------------------------------------------------
185
// OrganisationUnit Info
186
// ---------------------------------------------------------------------
187
List<Object> rimsSelectedOus = new ArrayList<Object>();
189
if ( includeDistrict != null )
191
// Get selected district and add it to the list of selected OUs
192
rimsSelectedOus.add( rimsService.getDistrict( rimsDistricts, connection ) );
194
else if ( selectedOrgunits == null )
196
throw new IllegalArgumentException( "No orgunits selected" );
199
if ( selectedOrgunits != null )
201
for ( String rimsOU: selectedOrgunits )
203
RIMS_PHC rimsOUObj = rimsService.getPHC( rimsOU, connection );
204
rimsSelectedOus.add( rimsOUObj );
209
List<RIMS_Mapping_DataElement> rimsSelectedMappingDEs = new ArrayList<RIMS_Mapping_DataElement>(
210
getSelectedMappingDEs() );
213
sDate = format.parseDate( startDate );
214
eDate = format.parseDate( endDate );
217
throw new IllegalArgumentException( endDate +" is not a valid date" );
219
// Add periods if necessary
220
checkAllPeriods( sDate, eDate );
221
List<Period> selectedPeriods = new ArrayList<Period>( periodService.getIntersectingPeriods( sDate, eDate ) );
225
// TODO Figure out this generics stuff
226
for ( Object o: rimsSelectedOus)
228
RIMSOrgUnit rimsOrgUnit = (RIMSOrgUnit) o;
229
int percentage = (int) (orgUnitI++ / (double) rimsSelectedOus.size() * 100);
230
System.out.print( "("+ percentage +" %) Importing "+ rimsOrgUnit.getName() );
232
RIMS_Mapping_Orgunit mappingOrgUnit = rimsService
233
.getMappingOrgUnit( rimsOrgUnit.getCode() );
235
for ( RIMS_Mapping_DataElement mappingDataElement: rimsSelectedMappingDEs)
237
System.out.print( '.' );
238
if ( mappingDataElement.getDhisExpression().equalsIgnoreCase( "NA" )
239
|| mappingDataElement.getDhisExpression().equalsIgnoreCase( "NO_SUCH_DE" ) )
241
System.out.println( "Skipped dataelement \""+ mappingDataElement.getDeName() +"\": marked as "+ mappingDataElement.getDhisExpression() );
245
if ( mappingDataElement.isTotal() && !includeTotals )
250
for ( Period period: selectedPeriods )
252
// Flip this using the debugger to abort.
253
// Eventually the progress indicator will have an abort button.
261
if ( period.getPeriodType().getName().equalsIgnoreCase(
264
if ( !config.getMonthlyFields().contains(
265
mappingDataElement.getRimsColumn() ) )
270
else if ( period.getPeriodType().getName()
271
.equalsIgnoreCase( "yearly" ) )
273
if ( !config.getYearlyFields().contains(
274
mappingDataElement.getRimsColumn() ) )
284
// -----------------------------------------------------
285
// Skip if mixing district/PHC data
286
// -----------------------------------------------------
287
RIMSTable table = config.getTable( mappingDataElement.getTableName() );
289
// Prevent superfluous "dataelement not found" errors
290
if ( ( table instanceof DistrictTable
291
&& rimsOrgUnit instanceof RIMS_PHC )
292
|| ( table instanceof PHCTable
293
&& rimsOrgUnit instanceof RIMSDistrict ) )
298
// -----------------------------------------------------
300
// -----------------------------------------------------
302
OrganisationUnit dhisOrgUnit = organisationUnitService
303
.getOrganisationUnit( Integer.parseInt(
304
mappingOrgUnit.getDhisid() ) );
305
if ( !dhisFormulaIsPlain( mappingDataElement
306
.getDhisExpression() ) )
308
skipped.add( new ImportExportError( mappingDataElement, mappingOrgUnit,
309
"Only plain formulas (no operators) are supported for import" ) );
312
// Get value from RIMS database
313
String rimsValue = getRIMSDataValue(
314
mappingDataElement, period, rimsOrgUnit, config );
316
if ( rimsValue == null )
319
DataElement dataElement = dataElementService
320
.getDataElement( mappingDataElement
321
.getDhisDataElementId() );
322
skipped.add( new ImportExportError( dataElement, mappingOrgUnit, period,
323
"Data value not found in RIMS" ) );
327
// ---------------------------------------------------------
329
// ---------------------------------------------------------
332
DataElement dataElement = dataElementService
333
.getDataElement( mappingDataElement
334
.getDhisDataElementId() );
335
if ( dataElement == null )
337
skipped.add( new ImportExportError( dataElement, mappingOrgUnit,
338
"Data element not found in DHIS" ) );
343
DataElementCategoryOptionCombo optionCombo = dataElementCategoryOptionComboService
344
.getDataElementCategoryOptionCombo( mappingDataElement
345
.getDhisOptionCombo() );
347
// If data value exists, replace it, otherwise create
349
DataValue dataValue = dataValueService.getDataValue(
350
dhisOrgUnit, dataElement, period, optionCombo );
352
if ( dataValue == null )
354
dataValue = new DataValue( dataElement, period,
355
dhisOrgUnit, rimsValue, config.getStoredBy(),
356
new Date(), null, optionCombo );
357
dataValueService.addDataValue( dataValue );
361
dataValue.setValue( rimsValue );
362
dataValue.setTimestamp( new Date() );
363
dataValue.setStoredBy( config.getStoredBy() );
365
dataValueService.updateDataValue( dataValue );
369
catch ( HibernateException hbe )
371
throw new Exception( "Database-access error", hbe );
373
catch ( Exception e )
375
// Log dataelement and reason
378
DataElement dataElement = dataElementService
379
.getDataElement( mappingDataElement
380
.getDhisDataElementId() );
381
skipped.add( new ImportExportError( dataElement,
382
mappingOrgUnit, period, e ) );
383
// TODO DEBUG: Beep added
384
System.err.println( e.getClass().getSimpleName() +": "+ e.getMessage() );
386
catch ( NumberFormatException f )
388
skipped.add( new ImportExportError( mappingDataElement,
389
mappingOrgUnit, period, e ) );
395
System.out.println( "done" );
398
statementManager.destroy();
402
// TODO A candidate for the DHIS core.
404
* Check that all required periods are present, and add them if necessary.
406
* @param start check periods after this.
407
* @param end check periods before this.
409
private void checkAllPeriods( Date start, Date end )
411
PeriodType monthly = new MonthlyPeriodType();
412
PeriodType yearly = new YearlyPeriodType();
414
// ---------------------------------------------------------------------
416
// ---------------------------------------------------------------------
418
// These calendars keep track of the first and last of each month as we iterate.
419
Calendar firstOfMonth = Calendar.getInstance();
420
Calendar lastOfMonth = Calendar.getInstance();
422
// Start with the month which startDate is in.
423
firstOfMonth.setTime( start );
424
firstOfMonth.set( DAY_OF_MONTH, 1 );
426
lastOfMonth.setTime( start );
427
lastOfMonth.set( DAY_OF_MONTH, lastOfMonth.getActualMaximum( DAY_OF_MONTH ) );
429
// Stop when firstOfMonth has passed endDate.
430
while( firstOfMonth.getTime().compareTo( end ) < 0 )
432
// Is the period present?
433
Period checkPeriod = periodService.getPeriod(
434
firstOfMonth.getTime(), lastOfMonth.getTime(), monthly );
435
if ( checkPeriod == null )
438
periodService.addPeriod( new Period(
439
monthly, firstOfMonth.getTime(), lastOfMonth.getTime() ) );
442
// Increment by one month.
443
firstOfMonth.add( MONTH, 1 );
444
lastOfMonth.add( MONTH, 1 );
445
lastOfMonth.set( DAY_OF_MONTH, lastOfMonth.getActualMaximum( DAY_OF_MONTH ) );
448
// ---------------------------------------------------------------------
450
// ---------------------------------------------------------------------
451
// These calendars keep track of the first and last of each month as we iterate.
452
Calendar firstOfYear = Calendar.getInstance();
453
Calendar lastOfYear = Calendar.getInstance();
455
// Start with the month which startDate is in.
456
firstOfYear.setTime( start );
457
firstOfYear.set( DAY_OF_YEAR, 1 );
459
lastOfYear.setTime( start );
460
lastOfYear.set( DAY_OF_YEAR, lastOfYear.getActualMaximum( DAY_OF_YEAR ) );
462
// Stop when firstOfYear has passed endDate.
463
while( firstOfYear.getTime().compareTo( end ) < 0 )
465
// Is the period present?
466
Period checkPeriod = periodService.getPeriod(
467
firstOfYear.getTime(), lastOfYear.getTime(), yearly);
468
if ( checkPeriod == null )
471
periodService.addPeriod( new Period(
472
yearly, firstOfYear.getTime(), lastOfYear.getTime() ) );
475
// Increment by one year.
476
firstOfYear.add( YEAR, 1 );
477
lastOfYear.add( YEAR, 1 );
478
lastOfYear.set( DAY_OF_YEAR, lastOfYear.getActualMaximum( DAY_OF_YEAR ) );
482
private String getRIMSDataValue(
483
RIMS_Mapping_DataElement mappingDataElement, Period period,
484
RIMSOrgUnit orgUnit, Configuration config ) throws SQLException
486
Calendar cal = Calendar.getInstance();
487
cal.setTime( period.getStartDate() );
488
int year = cal.get( Calendar.YEAR );
489
int month = cal.get( Calendar.MONTH );
492
RIMSTable table = config.getTable( mappingDataElement.getTableName() );
493
return table.getDataValue( mappingDataElement, orgUnit, month, year );
497
* Retrieve all selected dataelements for mapping from each dataelement group.
499
* @return a list of all dataelements from each group.
501
public List<RIMS_Mapping_DataElement> getSelectedMappingDEs() throws Exception
503
List<RIMS_Mapping_DataElement> rimsSelectedMappingsDes = new ArrayList<RIMS_Mapping_DataElement>();
505
for ( String sectionName: rimsDEGroups )
507
rimsSelectedMappingsDes.addAll( rimsService.getRIMSDataElementsByDEGroup( sectionName ) );
509
return rimsSelectedMappingsDes;
513
* Returns the PeriodType Object for selected DataElement, If no PeriodType
514
* is found then by default returns Monthly Period type
516
public PeriodType getDataElementPeriodType( DataElement de )
518
List<DataSet> dataSetList = new ArrayList<DataSet>( dataSetService.getAllDataSets() );
519
Iterator<DataSet> it = dataSetList.iterator();
520
while ( it.hasNext() )
522
DataSet ds = (DataSet) it.next();
523
List<DataElement> dataElementList = new ArrayList<DataElement>( ds.getDataElements() );
524
if ( dataElementList.contains( de ) )
526
return ds.getPeriodType();
530
return periodService.getPeriodTypeByName( "Monthly" );
531
} // getDataElementPeriodType end
533
private boolean dhisFormulaIsPlain( String formula )
535
// First check for the plain syntax (no operators)
536
String plain = "^\\[\\d+\\.\\d+\\]$";
537
if ( Pattern.matches( plain, formula ) )
541
// Then check that the formula has at least one data-element/option combo
542
String legal = "\\[\\d+\\.\\d+\\]";
543
if ( Pattern.matches( legal, formula ) )
547
throw new IllegalArgumentException( formula +" is not a valid dataelement expression" );
550
public Collection<ImportExportError> getSkipped()
555
public void setIncludeDistrict( String includeDistrict )
557
this.includeDistrict = includeDistrict;
560
public void setRimsDistricts( String rimsDistricts )
562
this.rimsDistricts = rimsDistricts;
565
public void setConnection( String connection )
567
this.connection = connection;