=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/caseaggregation/CaseAggregationConditionService.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/caseaggregation/CaseAggregationConditionService.java 2013-01-24 06:30:16 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/caseaggregation/CaseAggregationConditionService.java 2013-04-01 05:56:41 +0000 @@ -28,6 +28,7 @@ package org.hisp.dhis.caseaggregation; import java.util.Collection; +import java.util.List; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; @@ -62,7 +63,7 @@ Collection getCaseAggregationCondition( Collection dataElements ); - Integer parseConditition( CaseAggregationCondition aggregationCondition, OrganisationUnit orgunit, Period period ); + Double getAggregateValue( CaseAggregationCondition aggregationCondition, OrganisationUnit orgunit, Period period ); Collection getPatientDataValues( CaseAggregationCondition aggregationCondition, OrganisationUnit orgunit, Period period ); @@ -77,4 +78,7 @@ Collection getPatientAttributesInCondition( String aggregationExpression ); String getConditionDescription( String condition ); + + void aggregate( List caseAggregateSchedules, String taskStrategy ); + } === modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/caseaggregation/CaseAggregationConditionStore.java' --- dhis-2/dhis-api/src/main/java/org/hisp/dhis/caseaggregation/CaseAggregationConditionStore.java 2013-01-07 05:07:05 +0000 +++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/caseaggregation/CaseAggregationConditionStore.java 2013-04-01 05:56:41 +0000 @@ -29,10 +29,13 @@ import java.util.Collection; import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Future; import org.hisp.dhis.common.GenericNameableObjectStore; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; +import org.hisp.dhis.period.Period; /** * @author Chau Thu Tran @@ -43,12 +46,20 @@ extends GenericNameableObjectStore { String ID = CaseAggregationConditionStore.class.getName(); - + Collection get( DataElement dataElement ); CaseAggregationCondition get( DataElement dataElement, DataElementCategoryOptionCombo optionCombo ); - + Collection get( Collection dataElements ); - + List executeSQL( String sql ); + + String parseExpressionToSql( String aggregationExpression, String operator, String deType, Integer deSumId, + Integer orgunitId, String startDate, String endDate ); + + Double getAggregateValue( String caseExpression, String operator, String deType, Integer deSumId, + Integer orgunitId, Period period ); + + Future aggregate( ConcurrentLinkedQueue dataSetIds, String taskStrategy ); } === modified file 'dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/caseaggregation/DefaultCaseAggregationConditionService.java' --- dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/caseaggregation/DefaultCaseAggregationConditionService.java 2013-02-20 13:46:42 +0000 +++ dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/caseaggregation/DefaultCaseAggregationConditionService.java 2013-04-01 05:56:41 +0000 @@ -27,7 +27,6 @@ package org.hisp.dhis.caseaggregation; -import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.AGGRERATION_COUNT; import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.AGGRERATION_SUM; import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PATIENT; import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PATIENT_ATTRIBUTE; @@ -38,7 +37,6 @@ import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PROGRAM_STAGE; import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PROGRAM_STAGE_DATAELEMENT; import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PROGRAM_STAGE_PROPERTY; -import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OPERATOR_AND; import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.SEPARATOR_ID; import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.SEPARATOR_OBJECT; import static org.hisp.dhis.i18n.I18nUtils.i18n; @@ -47,6 +45,8 @@ import java.util.Collection; import java.util.HashSet; import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Future; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -54,7 +54,6 @@ import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; import org.hisp.dhis.dataelement.DataElementService; import org.hisp.dhis.i18n.I18nService; -import org.hisp.dhis.jdbc.StatementBuilder; import org.hisp.dhis.organisationunit.OrganisationUnit; import org.hisp.dhis.patient.Patient; import org.hisp.dhis.patient.PatientAttribute; @@ -69,8 +68,9 @@ import org.hisp.dhis.program.ProgramStageInstance; import org.hisp.dhis.program.ProgramStageInstanceService; import org.hisp.dhis.program.ProgramStageService; +import org.hisp.dhis.system.util.ConcurrentUtils; import org.hisp.dhis.system.util.DateUtils; -import org.nfunk.jep.JEP; +import org.hisp.dhis.system.util.SystemUtils; import org.springframework.transaction.annotation.Transactional; /** @@ -89,22 +89,12 @@ + OBJECT_PROGRAM_PROPERTY + ")" + SEPARATOR_OBJECT + "([a-zA-Z0-9@#\\- ]+[" + SEPARATOR_ID + "[a-zA-Z0-9]*]*)" + "\\]"; - private final String IS_NULL = "is null"; - - private final String PROPERTY_AGE = "age"; - private final String INVALID_CONDITION = "Invalid condition"; private final String TOTAL_OF_PATIENTS_REGISTERED = "Total of patient registration"; private final String IN_CONDITION_GET_ALL = "*"; - private final String IN_CONDITION_START_SIGN = "@"; - - private final String IN_CONDITION_END_SIGN = "#"; - - private final String IN_CONDITION_COUNT_X_TIMES = "COUNT"; - // ------------------------------------------------------------------------- // Dependencies // ------------------------------------------------------------------------- @@ -125,19 +115,12 @@ private ProgramStageInstanceService programStageInstanceService; - private StatementBuilder statementBuilder; - private I18nService i18nService; // ------------------------------------------------------------------------- // Getters && Setters // ------------------------------------------------------------------------- - public void setStatementBuilder( StatementBuilder statementBuilder ) - { - this.statementBuilder = statementBuilder; - } - public void setAggregationConditionStore( CaseAggregationConditionStore aggregationConditionStore ) { this.aggregationConditionStore = aggregationConditionStore; @@ -232,36 +215,15 @@ } @Override - public Integer parseConditition( CaseAggregationCondition aggregationCondition, OrganisationUnit orgunit, + public Double getAggregateValue( CaseAggregationCondition aggregationCondition, OrganisationUnit orgunit, Period period ) { - String operator = aggregationCondition.getOperator(); - - if ( operator.equals( CaseAggregationCondition.AGGRERATION_COUNT ) - || operator.equals( CaseAggregationCondition.AGGRERATION_SUM ) ) - { - String sql = convertCondition( aggregationCondition, orgunit, period ); - Collection ids = aggregationConditionStore.executeSQL( sql ); - return (ids == null) ? null : ids.size(); - } - - String sql = "SELECT " + operator + "( cast( pdv.value as DOUBLE PRECISION ) ) "; - sql += "FROM patientdatavalue pdv "; - sql += " INNER JOIN programstageinstance psi "; - sql += " ON psi.programstageinstanceid = pdv.programstageinstanceid "; - sql += "WHERE executiondate >='" + DateUtils.getMediumDateString( period.getStartDate() ) + "' "; - sql += " AND executiondate <='" + DateUtils.getMediumDateString( period.getEndDate() ) - + "' AND pdv.dataelementid=" + aggregationCondition.getDeSum().getId(); - - if ( aggregationCondition.getAggregationExpression() != null - && !aggregationCondition.getAggregationExpression().isEmpty() ) - { - sql = sql + " AND pdv.programstageinstanceid in ( " - + convertCondition( aggregationCondition, orgunit, period ) + " ) "; - } - - Collection ids = aggregationConditionStore.executeSQL( sql ); - return (ids == null) ? null : ids.iterator().next(); + DataElement aggDataElement = aggregationCondition.getAggregationDataElement(); + DataElement deSum = aggregationCondition.getDeSum(); + Integer deSumId = (deSum == null) ? null : deSum.getId(); + + return aggregationConditionStore.getAggregateValue( aggregationCondition.getAggregationExpression(), + aggregationCondition.getOperator(), aggDataElement.getType(), deSumId, orgunit.getId(), period ); } @Override @@ -269,14 +231,18 @@ OrganisationUnit orgunit, Period period ) { // get params + int orgunitId = orgunit.getId(); String startDate = DateUtils.getMediumDateString( period.getStartDate() ); String endDate = DateUtils.getMediumDateString( period.getEndDate() ); + DataElement aggDataElement = aggregationCondition.getAggregationDataElement(); + DataElement deSum = aggregationCondition.getDeSum(); + Integer deSumId = (deSum == null) ? null : deSum.getId(); Collection result = new HashSet(); - String sql = createSQL( aggregationCondition.getAggregationExpression(), aggregationCondition.getOperator(), - orgunitId, startDate, endDate ); + String sql = aggregationConditionStore.parseExpressionToSql( aggregationCondition.getAggregationExpression(), + aggregationCondition.getOperator(), aggDataElement.getType(), deSumId, orgunitId, startDate, endDate ); Collection dataElements = getDataElementsInCondition( aggregationCondition .getAggregationExpression() ); @@ -302,10 +268,18 @@ public Collection getPatients( CaseAggregationCondition aggregationCondition, OrganisationUnit orgunit, Period period ) { + DataElement aggDataElement = aggregationCondition.getAggregationDataElement(); + DataElement deSum = aggregationCondition.getDeSum(); + Integer deSumId = (deSum == null) ? null : deSum.getId(); + + String sql = aggregationConditionStore + .parseExpressionToSql( aggregationCondition.getAggregationExpression(), aggregationCondition.getOperator(), + aggDataElement.getType(), deSumId, orgunit.getId(), + DateUtils.getMediumDateString( period.getStartDate() ), + DateUtils.getMediumDateString( period.getEndDate() ) ); + Collection result = new HashSet(); - String sql = convertCondition( aggregationCondition, orgunit, period ); - Collection patientIds = aggregationConditionStore.executeSQL( sql ); if ( patientIds != null ) @@ -335,8 +309,15 @@ || operator.equals( CaseAggregationCondition.AGGRERATION_SUM ) ) { aggregationCondition.setOperator( AGGRERATION_SUM ); - sql = createSQL( aggregationCondition.getAggregationExpression(), aggregationCondition.getOperator(), - orgunitId, startDate, endDate ); + + // get params + + DataElement aggDataElement = aggregationCondition.getAggregationDataElement(); + DataElement deSum = aggregationCondition.getDeSum(); + Integer deSumId = (deSum == null) ? null : deSum.getId(); + + sql = aggregationConditionStore.parseExpressionToSql( aggregationCondition.getAggregationExpression(), + aggregationCondition.getOperator(), aggDataElement.getType(), deSumId, orgunitId, startDate, endDate ); } else { @@ -351,8 +332,21 @@ if ( aggregationCondition.getAggregationExpression() != null && !aggregationCondition.getAggregationExpression().isEmpty() ) { - sql = sql + " AND pdv.programstageinstanceid in ( " - + convertCondition( aggregationCondition, orgunit, period ) + " ) "; + sql = sql + " AND pdv.programstageinstanceid in ( "; + + // Get params + + DataElement aggDataElement = aggregationCondition.getAggregationDataElement(); + DataElement deSum = aggregationCondition.getDeSum(); + Integer deSumId = (deSum == null) ? null : deSum.getId(); + + String conditionSql = aggregationConditionStore.parseExpressionToSql( + aggregationCondition.getAggregationExpression(), aggregationCondition.getOperator(), + aggDataElement.getType(), deSumId, orgunit.getId(), + DateUtils.getMediumDateString( period.getStartDate() ), + DateUtils.getMediumDateString( period.getEndDate() ) ); + + sql += conditionSql + " ) "; } } @@ -567,491 +561,28 @@ return i18n( i18nService, aggregationConditionStore.get( dataElements ) ); } + public void aggregate( List caseAggregateSchedules, String taskStrategy ) + { + ConcurrentLinkedQueue datasetQ = new ConcurrentLinkedQueue( + caseAggregateSchedules ); + + List> futures = new ArrayList>(); + + for ( int i = 0; i < getProcessNo(); i++ ) + { + futures.add( aggregationConditionStore.aggregate( datasetQ, taskStrategy ) ); + } + + ConcurrentUtils.waitForCompletion( futures ); + } + // ------------------------------------------------------------------------- // Support Methods // ------------------------------------------------------------------------- - private String convertCondition( CaseAggregationCondition aggregationCondition, OrganisationUnit orgunit, - Period period ) - { - // get params - int orgunitId = orgunit.getId(); - String startDate = DateUtils.getMediumDateString( period.getStartDate() ); - String endDate = DateUtils.getMediumDateString( period.getEndDate() ); - - // Get operators between ( ) - Pattern patternOperator = Pattern.compile( "(\\)\\s*(OR|AND)\\s*\\( )" ); - - Matcher matcherOperator = patternOperator.matcher( aggregationCondition.getAggregationExpression() ); - - List operators = new ArrayList(); - - while ( matcherOperator.find() ) - { - operators.add( matcherOperator.group( 2 ) ); - } - - List subSQL = new ArrayList(); - - String[] conditions = aggregationCondition.getAggregationExpression().split( "(\\)\\s*(OR|AND)\\s*\\()" ); - - // Create SQL statement for the first condition - String condition = conditions[0].replace( "(", "" ).replace( ")", "" ); - - String sql = createSQL( condition, aggregationCondition.getOperator(), orgunitId, startDate, endDate ); - - subSQL.add( sql ); - - // Create SQL statement for others - for ( int index = 1; index < conditions.length; index++ ) - { - condition = conditions[index].replace( "(", "" ).replace( ")", "" ); - - sql = "(" + createSQL( condition, aggregationCondition.getOperator(), orgunitId, startDate, endDate ) + ")"; - - subSQL.add( sql ); - } - - sql = getSQL( aggregationCondition.getOperator(), subSQL, operators ).replace( IN_CONDITION_START_SIGN, "(" ) - .replaceAll( IN_CONDITION_END_SIGN, ")" ); - return sql; - } - - private String createSQL( String aggregationExpression, String operator, int orgunitId, String startDate, - String endDate ) - { - // --------------------------------------------------------------------- - // get operators - // --------------------------------------------------------------------- - - Pattern patternOperator = Pattern.compile( "(AND|OR)" ); - - Matcher matcherOperator = patternOperator.matcher( aggregationExpression ); - - List operators = new ArrayList(); - - while ( matcherOperator.find() ) - { - operators.add( matcherOperator.group() ); - } - - String[] expression = aggregationExpression.split( "(AND|OR)" ); - - // --------------------------------------------------------------------- - // parse expressions - // --------------------------------------------------------------------- - - Pattern patternCondition = Pattern.compile( regExp ); - - List conditions = new ArrayList(); - double value = 0.0; - - for ( int i = 0; i < expression.length; i++ ) - { - String subExp = expression[i]; - List subConditions = new ArrayList(); - - Matcher matcherCondition = patternCondition.matcher( expression[i] ); - - String condition = ""; - - while ( matcherCondition.find() ) - { - String match = matcherCondition.group(); - subExp = subExp.replace( match, "~" ); - match = match.replaceAll( "[\\[\\]]", "" ); - - String[] info = match.split( SEPARATOR_OBJECT ); - - if ( info[0].equalsIgnoreCase( OBJECT_PATIENT ) ) - { - condition = getConditionForPatient( orgunitId, operator, startDate, endDate ); - } - else if ( info[0].equalsIgnoreCase( OBJECT_PATIENT_PROPERTY ) ) - { - String propertyName = info[1]; - condition = getConditionForPatientProperty( propertyName, operator, startDate, endDate ); - - } - else if ( info[0].equalsIgnoreCase( OBJECT_PATIENT_ATTRIBUTE ) ) - { - int attributeId = Integer.parseInt( info[1] ); - condition = getConditionForPatientAttribute( attributeId, operator ); - } - else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM_STAGE_DATAELEMENT ) ) - { - String[] ids = info[1].split( SEPARATOR_ID ); - - int programId = Integer.parseInt( ids[0] ); - String programStageId = ids[1]; - int dataElementId = Integer.parseInt( ids[2] ); - - String valueToCompare = expression[i].replace( "[" + match + "]", "" ).trim(); - - if ( valueToCompare.equalsIgnoreCase( IS_NULL ) ) - { - condition = getConditionForNotDataElement( programId, programStageId, operator, dataElementId, - orgunitId, startDate, endDate ); - - expression[i] = expression[i].replace( valueToCompare, "" ); - } - else - { - condition = getConditionForDataElement( programId, programStageId, operator, dataElementId, - orgunitId, startDate, endDate ); - DataElement dataElement = dataElementService.getDataElement( dataElementId ); - if ( !expression[i].contains( "+" ) ) - { - if ( dataElement.getType().equals( DataElement.VALUE_TYPE_INT ) ) - { - condition += " AND cast( pd.value as " + statementBuilder.getDoubleColumnType() + ") "; - } - else - { - condition += " AND pd.value "; - } - } - else - { - subConditions.add( condition ); - } - } - } - - else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM_PROPERTY ) ) - { - condition = getConditionForProgramProperty( operator, startDate, endDate, info[1] ); - } - else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM ) ) - { - String[] ids = info[1].split( SEPARATOR_ID ); - condition = getConditionForProgram( ids[0], operator, orgunitId, startDate, endDate ); - if ( ids.length > 1 ) - { - condition += ids[1]; - } - } - else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM_STAGE ) ) - { - String[] ids = info[1].split( SEPARATOR_ID ); - if ( ids.length == 2 && ids[1].equals( IN_CONDITION_COUNT_X_TIMES ) ) - { - condition = getConditionForCountProgramStage( ids[0], operator, orgunitId, startDate, endDate ); - } - else - { - condition = getConditionForProgramStage( ids[0], operator, orgunitId, startDate, endDate ); - } - } - else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM_STAGE_PROPERTY ) ) - { - condition = getConditionForProgramStageProperty( info[1], operator, orgunitId, startDate, endDate ); - } - else if ( info[0].equalsIgnoreCase( OBJECT_PATIENT_PROGRAM_STAGE_PROPERTY ) ) - { - condition = getConditionForPatientProgramStageProperty( info[1], operator, startDate, endDate ); - } - - // ------------------------------------------------------------- - // Replacing the operand with 1 in order to later be able to - // verify - // that the formula is mathematically valid - // ------------------------------------------------------------- - - if ( expression[i].contains( "+" ) ) - { - Collection patientIds = aggregationConditionStore.executeSQL( condition ); - value = calValue( patientIds, AGGRERATION_SUM ); - - subExp = subExp.replace( "~", value + "" ); - } - - condition = expression[i].replace( match, condition ).replaceAll( "[\\[\\]]", "" ); - } - - if ( expression[i].contains( "+" ) ) - { - final JEP parser = new JEP(); - - parser.parseExpression( subExp ); - - String _subExp = (parser.getValue() == 1.0) ? " AND 1 = 1 " : " AND 0 = 1 "; - - int noPlus = expression[i].split( "\\+" ).length - 1; - List subOperators = new ArrayList(); - for ( int j = 0; j < noPlus; j++ ) - { - subOperators.add( "AND" ); - } - - condition = getSQL( operator, subConditions, subOperators ) + _subExp; - } - - conditions.add( condition ); - } - - return getSQL( operator, conditions, operators ); - } - - private String getConditionForNotDataElement( int programId, String programStageId, String operator, - int dataElementId, int orgunitId, String startDate, String endDate ) - { - String sql = "SELECT distinct(pi.patientid) "; - String from = "FROM programstageinstance as psi " - + "INNER JOIN programinstance as pi ON pi.programinstanceid = psi.programinstanceid "; - - String condition = "pi.patientid "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - sql = "SELECT psi.programstageinstanceid "; - condition = "psi.programstageinstanceid "; - } - - sql += from - + "LEFT OUTER JOIN patientdatavalue as pd ON psi.programstageinstanceid = pd.programstageinstanceid " - + "WHERE psi.executionDate >= '" + startDate + "' AND psi.executionDate <= '" + endDate + "' " - + "AND pd.value IS NULL AND " + condition + " NOT IN ( " + "SELECT " + condition + from - + "WHERE psi.organisationunitid = " + orgunitId + " AND pi.programid = " + programId + " " - + "AND psi.executionDate >= '" + startDate + "' AND psi.executionDate <= '" + endDate + "' " - + "AND pd.dataelementid = " + dataElementId + " "; - - if ( !programStageId.equals( IN_CONDITION_GET_ALL ) ) - { - sql += " AND psi.programstageid = " + programStageId; - } - - return sql + " ) "; - } - - private String getConditionForDataElement( int programId, String programStageId, String operator, - int dataElementId, int orgunitId, String startDate, String endDate ) - { - String sql = "SELECT distinct(pi.patientid) "; - String from = "FROM programstageinstance as psi " - + "INNER JOIN patientdatavalue as pd ON psi.programstageinstanceid = pd.programstageinstanceid " - + "INNER JOIN programinstance as pi ON pi.programinstanceid = psi.programinstanceid "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - sql = "SELECT psi.programstageinstanceid "; - from = "FROM programstageinstance as psi " - + "INNER JOIN patientdatavalue as pd ON psi.programstageinstanceid = pd.programstageinstanceid "; - } - - sql += from + " WHERE pd.dataelementid=" + dataElementId + " AND psi.organisationunitid=" + orgunitId - + " AND psi.executionDate>='" + startDate + "' AND psi.executionDate <= '" + endDate + "'"; - - if ( !programStageId.equals( IN_CONDITION_GET_ALL ) ) - { - sql += " AND psi.programstageid = " + programStageId; - } - - return sql; - } - - private String getConditionForPatientAttribute( int attributeId, String operator ) - { - String sql = "SELECT distinct(pi.patientid) "; - String from = "FROM patientattributevalue pi "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - sql = "SELECT psi.programstageinstanceid "; - from = "FROM programstageinstance psi inner join programinstance pi " - + "on psi.programinstanceid=pi.programinstanceid " + "inner join patientattributevalue pav " - + "on pav.patientid=pi.patientid "; - } - - return sql + from + "WHERE patientattributeid=" + attributeId + " AND value "; - } - - private String getConditionForPatient( int orgunitId, String operator, String startDate, String endDate ) - { - String sql = "SELECT pi.patientid "; - String from = "FROM patient pi "; - String where = "WHERE pi.organisationunitid=" + orgunitId + " AND pi.registrationdate>= '" + startDate + "' " - + "AND pi.registrationdate <= '" + endDate + "'"; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - sql = "SELECT psi.programstageinstanceid "; - from = "FROM programstageinstance psi inner join programinstance pi " - + "on psi.programinstanceid=pi.programinstanceid " - + "inner join patient p on p.patientid=pi.patientid "; - where = "WHERE p.organisationunitid=" + orgunitId + " AND p.registrationdate>= '" + startDate + "' " - + "AND p.registrationdate <= '" + endDate + "'"; - } - - return sql + from + where; - } - - private String getConditionForPatientProperty( String propertyName, String operator, String startDate, - String endDate ) - { - String sql = "SELECT distinct(pi.patientid) FROM patient pi WHERE "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - sql = "SELECT psi.programstageinstanceid " + "FROM programstageinstance psi inner join programinstance pi " - + "on psi.programinstanceid=pi.programinstanceid " - + "inner join patient p on p.patientid=pi.patientid WHERE "; - } - - if ( propertyName.equals( PROPERTY_AGE ) ) - { - sql += "DATE(registrationdate) - DATE(birthdate) "; - } - else - { - sql += propertyName + " "; - } - - return sql; - } - - private String getConditionForPatientProgramStageProperty( String propertyName, String operator, String startDate, - String endDate ) - { - String sql = "SELECT distinct(pi.patientid) "; - String from = "FROM programinstance pi INNER JOIN programstageinstance psi " - + "ON psi.programinstanceid=pi.programinstanceid "; - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - sql = "SELECT psi.programstageinstance "; - from = "FROM programstageinstance psi "; - } - - from += "inner join patient p on p.patientid=pi.patientid "; - - sql += from + "WHERE executionDate>='" + startDate + "' and executionDate<='" + endDate + "' and " - + propertyName; - - return sql; - } - - private String getConditionForProgramProperty( String operator, String startDate, String endDate, String property ) - { - String sql = "SELECT pi.patientid FROM programinstance as pi "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - sql = "SELECT psi.programstageinstanceid FROM programinstance as pi " - + "INNER JOIN programstageinstance psi ON psi.programinstanceid=pi.programinstanceid "; - } - - return sql + "WHERE pi.enrollmentdate>='" + startDate + "' " + "AND pi.enrollmentdate<='" + endDate + "' AND " - + property; - } - - private String getConditionForProgram( String programId, String operator, int orgunitId, String startDate, - String endDate ) - { - String sql = "SELECT distinct(pi.patientid) FROM programinstance as pi " - + "inner join patient psi on psi.patientid=pi.patientid "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - sql = "SELECT psi.programstageinstanceid FROM programinstance as pi " - + "INNER JOIN programstageinstance psi ON pi.programinstanceid=psi.programinstanceid "; - } - - return sql + "WHERE pi.programid=" + programId + " " + " AND psi.organisationunitid = " + orgunitId - + " AND pi.enrollmentdate >= '" + startDate + "' AND pi.enrollmentdate <= '" + endDate + "' "; - } - - private String getConditionForProgramStage( String programStageId, String operator, int orgunitId, - String startDate, String endDate ) - { - String select = "SELECT distinct(pi.patientid) "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - select = "SELECT psi.programstageinstanceid "; - } - - return select + "FROM programinstance as pi INNER JOIN programstageinstance psi " - + "ON pi.programinstanceid = psi.programinstanceid WHERE psi.programstageid=" + programStageId + " " - + "AND psi.executiondate >= '" + startDate + "' AND psi.executiondate <= '" + endDate - + "' AND psi.organisationunitid = " + orgunitId + " "; - } - - private String getConditionForCountProgramStage( String programStageId, String operator, int orgunitId, - String startDate, String endDate ) - { - String select = "SELECT distinct(pi.patientid) "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - select = "SELECT psi.programstageinstanceid "; - } - - select += "FROM programstageinstance as psi " - + "INNER JOIN programinstance as pi ON pi.programinstanceid = psi.programinstanceid " - + "WHERE psi.organisationunitid = " + orgunitId + " and psi.programstageid = " + programStageId + " " - + "AND psi.executionDate >= '" + startDate + "' AND psi.executionDate <= '" + endDate + "' " - + "GROUP BY psi.programinstanceid "; - - if ( operator.equals( AGGRERATION_COUNT ) ) - { - select += ",pi.patientid "; - } - - select += "HAVING count(psi.programstageinstanceid) "; - - return select; - - } - - private String getConditionForProgramStageProperty( String property, String operator, int orgunitId, - String startDate, String endDate ) - { - String select = "SELECT distinct(pi.patientid) "; - - if ( !operator.equals( AGGRERATION_COUNT ) ) - { - select = "SELECT psi.programstageinstanceid "; - } - - return select + "FROM programinstance as pi INNER JOIN programstageinstance psi " - + "ON pi.programinstanceid = psi.programinstanceid WHERE " + " psi.executiondate >= '" + startDate - + "' AND psi.executiondate <= '" + endDate + "' AND psi.organisationunitid = " + orgunitId + " AND " - + property; - } - - private String getSQL( String aggregateOperator, List conditions, List operators ) - { - String sql = conditions.get( 0 ); - - String sqlAnd = ""; - - int index = 0; - - for ( index = 0; index < operators.size(); index++ ) - { - if ( operators.get( index ).equalsIgnoreCase( OPERATOR_AND ) ) - { - if ( aggregateOperator.equals( AGGRERATION_COUNT ) ) - { - sql += " AND pi.patientid IN ( " + conditions.get( index + 1 ); - } - else - { - sql += " AND psi.programstageinstanceid IN ( " + conditions.get( index + 1 ); - } - sqlAnd += ")"; - } - else - { - sql += sqlAnd; - sql += " UNION ( " + conditions.get( index + 1 ) + " ) "; - sqlAnd = ""; - } - } - - sql += sqlAnd; - - return sql; + private int getProcessNo() + { + return Math.max( (SystemUtils.getCpuCores() - 1), 1 ); } public Integer calValue( Collection patientIds, String operator ) === modified file 'dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/caseaggregation/jdbc/JdbcCaseAggregationConditionStore.java' --- dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/caseaggregation/jdbc/JdbcCaseAggregationConditionStore.java 2013-01-15 10:28:17 +0000 +++ dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/caseaggregation/jdbc/JdbcCaseAggregationConditionStore.java 2013-04-01 05:56:41 +0000 @@ -27,19 +27,55 @@ package org.hisp.dhis.caseaggregation.jdbc; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.AGGRERATION_COUNT; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PATIENT; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PATIENT_ATTRIBUTE; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PATIENT_PROGRAM_STAGE_PROPERTY; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PATIENT_PROPERTY; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PROGRAM; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PROGRAM_PROPERTY; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PROGRAM_STAGE; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PROGRAM_STAGE_DATAELEMENT; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OBJECT_PROGRAM_STAGE_PROPERTY; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.OPERATOR_AND; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.SEPARATOR_ID; +import static org.hisp.dhis.caseaggregation.CaseAggregationCondition.SEPARATOR_OBJECT; +import static org.hisp.dhis.patient.scheduling.CaseAggregateConditionSchedulingManager.TASK_AGGREGATE_QUERY_BUILDER_LAST_12_MONTH; +import static org.hisp.dhis.patient.scheduling.CaseAggregateConditionSchedulingManager.TASK_AGGREGATE_QUERY_BUILDER_LAST_3_MONTH; +import static org.hisp.dhis.patient.scheduling.CaseAggregateConditionSchedulingManager.TASK_AGGREGATE_QUERY_BUILDER_LAST_6_MONTH; +import static org.hisp.dhis.patient.scheduling.CaseAggregateConditionSchedulingManager.TASK_AGGREGATE_QUERY_BUILDER_LAST_MONTH; + import java.sql.ResultSet; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.Calendar; import java.util.Collection; +import java.util.Date; +import java.util.HashSet; import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Future; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import org.hibernate.criterion.Restrictions; +import org.hisp.dhis.caseaggregation.CaseAggregateSchedule; import org.hisp.dhis.caseaggregation.CaseAggregationCondition; import org.hisp.dhis.caseaggregation.CaseAggregationConditionStore; import org.hisp.dhis.common.hibernate.HibernateIdentifiableObjectStore; import org.hisp.dhis.dataelement.DataElement; import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; +import org.hisp.dhis.jdbc.StatementBuilder; +import org.hisp.dhis.period.CalendarPeriodType; +import org.hisp.dhis.period.Period; +import org.hisp.dhis.period.PeriodService; +import org.hisp.dhis.program.ProgramStageInstanceService; +import org.hisp.dhis.system.util.DateUtils; +import org.nfunk.jep.JEP; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.support.rowset.SqlRowSet; +import org.springframework.scheduling.annotation.Async; /** * @author Chau Thu Tran @@ -50,21 +86,56 @@ extends HibernateIdentifiableObjectStore implements CaseAggregationConditionStore { + private final String regExp = "\\[(" + OBJECT_PATIENT + "|" + OBJECT_PROGRAM + "|" + OBJECT_PROGRAM_STAGE + "|" + + OBJECT_PROGRAM_STAGE_PROPERTY + "|" + OBJECT_PATIENT_PROGRAM_STAGE_PROPERTY + "|" + + OBJECT_PROGRAM_STAGE_DATAELEMENT + "|" + OBJECT_PATIENT_ATTRIBUTE + "|" + OBJECT_PATIENT_PROPERTY + "|" + + OBJECT_PROGRAM_PROPERTY + ")" + SEPARATOR_OBJECT + "([a-zA-Z0-9@#\\- ]+[" + SEPARATOR_ID + "[a-zA-Z0-9]*]*)" + + "\\]"; + + private final String IS_NULL = "is null"; + + private final String PROPERTY_AGE = "age"; + + private final String IN_CONDITION_GET_ALL = "*"; + + private final String IN_CONDITION_START_SIGN = "@"; + + private final String IN_CONDITION_END_SIGN = "#"; + + private final String IN_CONDITION_COUNT_X_TIMES = "COUNT"; + // ------------------------------------------------------------------------- // Dependency // ------------------------------------------------------------------------- private JdbcTemplate jdbcTemplate; - // ------------------------------------------------------------------------- - // Setters - // ------------------------------------------------------------------------- - public void setJdbcTemplate( JdbcTemplate jdbcTemplate ) { this.jdbcTemplate = jdbcTemplate; } + private StatementBuilder statementBuilder; + + public void setStatementBuilder( StatementBuilder statementBuilder ) + { + this.statementBuilder = statementBuilder; + } + + private ProgramStageInstanceService programStageInstanceService; + + public void setProgramStageInstanceService( ProgramStageInstanceService programStageInstanceService ) + { + this.programStageInstanceService = programStageInstanceService; + } + + private PeriodService periodService; + + public void setPeriodService( PeriodService periodService ) + { + this.periodService = periodService; + } + // ------------------------------------------------------------------------- // Implementation Methods // ------------------------------------------------------------------------- @@ -82,7 +153,7 @@ return rs.getInt( 1 ); } } ); - + return patientIds; } catch ( Exception ex ) @@ -112,4 +183,700 @@ { return getCriteria( Restrictions.in( "aggregationDataElement", dataElements ) ).list(); } + + public static final String STORED_BY_DHIS_SYSTEM = "DHIS-System"; + + @Async + public Future aggregate( ConcurrentLinkedQueue caseAggregateSchedule, String taskStrategy ) + { + taskLoop: while ( true ) + { + CaseAggregateSchedule dataSet = caseAggregateSchedule.poll(); + + if ( dataSet == null ) + { + break taskLoop; + } + + Collection periods = getPeriods( dataSet.getPeriodTypeName(), taskStrategy ); + + runAggregate( null, dataSet, periods ); + } + return null; + } + + + public Double getAggregateValue( String caseExpression, String operator, String deType, Integer deSumId, + Integer orgunitId, Period period ) + { + String startDate = DateUtils.getMediumDateString( period.getStartDate() ); + String endDate = DateUtils.getMediumDateString( period.getEndDate() ); + + if ( operator.equals( CaseAggregationCondition.AGGRERATION_COUNT ) + || operator.equals( CaseAggregationCondition.AGGRERATION_SUM ) ) + { + String sql = parseExpressionToSql( caseExpression, operator, deType, deSumId, orgunitId, startDate, endDate ); + Collection ids = this.executeSQL( sql ); + return (ids == null) ? null : ids.size() + 0.0; + } + + String sql = "SELECT " + operator + "( cast( pdv.value as DOUBLE PRECISION ) ) "; + sql += "FROM patientdatavalue pdv "; + sql += " INNER JOIN programstageinstance psi "; + sql += " ON psi.programstageinstanceid = pdv.programstageinstanceid "; + sql += "WHERE executiondate >='" + DateUtils.getMediumDateString( period.getStartDate() ) + "' "; + sql += " AND executiondate <='" + DateUtils.getMediumDateString( period.getEndDate() ) + + "' AND pdv.dataelementid=" + deSumId; + + if ( caseExpression != null && !caseExpression.isEmpty() ) + { + sql = sql + " AND pdv.programstageinstanceid in ( " + + parseExpressionToSql( caseExpression, operator, deType, deSumId, orgunitId, startDate, endDate ) + " ) "; + } + + Collection ids = this.executeSQL( sql ); + return (ids == null) ? null : ids.iterator().next() + 0.0; + } + + public String parseExpressionToSql( String aggregationExpression, String operator, String deType, Integer deSumId, + Integer orgunitId, String startDate, String endDate ) + { + // Get operators between ( ) + Pattern patternOperator = Pattern.compile( "(\\)\\s*(OR|AND)\\s*\\( )" ); + + Matcher matcherOperator = patternOperator.matcher( aggregationExpression ); + + List operators = new ArrayList(); + + while ( matcherOperator.find() ) + { + operators.add( matcherOperator.group( 2 ) ); + } + + List subSQL = new ArrayList(); + + String[] conditions = aggregationExpression.split( "(\\)\\s*(OR|AND)\\s*\\()" ); + + // Create SQL statement for the first condition + String condition = conditions[0].replace( "(", "" ).replace( ")", "" ); + + String sql = createSQL( condition, operator, deType, orgunitId, startDate, endDate ); + + subSQL.add( sql ); + + // Create SQL statement for others + for ( int index = 1; index < conditions.length; index++ ) + { + condition = conditions[index].replace( "(", "" ).replace( ")", "" ); + + sql = "(" + createSQL( condition, operator, deType, orgunitId, startDate, endDate ) + ")"; + + subSQL.add( sql ); + } + + sql = getSQL( operator, subSQL, operators ).replace( IN_CONDITION_START_SIGN, "(" ).replaceAll( + IN_CONDITION_END_SIGN, ")" ); + return sql; + } + + // ------------------------------------------------------------------------- + // Supportive methods + // ------------------------------------------------------------------------- + + private void runAggregate( Collection orgunitIds, CaseAggregateSchedule dataSet, Collection periods ) + { + String sql = "select caseaggregationconditionid, aggregationdataelementid, optioncomboid, de.valuetype as deType, " + + " cagg.aggregationexpression as caseexpression, cagg.\"operator\" as caseoperator, cagg.desum as desumid " + + " from caseaggregationcondition cagg inner join datasetmembers dm " + + " on cagg.aggregationdataelementid=dm.dataelementid " + + " inner join dataset ds " + + " on ds.datasetid = dm.datasetid " + + " inner join periodtype pt " + + " on pt.periodtypeid=ds.periodtypeid " + + " inner join dataelement de " + + " on de.dataelementid=dm.dataelementid " + + " where ds.datasetid = " + + dataSet.getDataSetId(); + + SqlRowSet rs = jdbcTemplate.queryForRowSet( sql ); + + while ( rs.next() ) + { + for ( Period period : periods ) + { + // ------------------------------------------------------------- + // Get formula, agg-dataelement and option-combo + // ------------------------------------------------------------- + + int dataelementId = rs.getInt( "aggregationdataelementid" ); + int optionComboId = rs.getInt( "optioncomboid" ); + String caseExpression = rs.getString( "caseexpression" ); + String caseOperator = rs.getString( "caseoperator" ); + String deType = rs.getString( "deType" ); + int deSumId = rs.getInt( "desumid" ); + + Collection _orgunitIds = programStageInstanceService.getOrganisationUnitIds( + period.getStartDate(), period.getEndDate() ); + if ( orgunitIds == null ) + { + orgunitIds = new HashSet(); + orgunitIds.addAll( _orgunitIds ); + } + else + { + orgunitIds.retainAll( _orgunitIds ); + } + // --------------------------------------------------------------------- + // Aggregation + // --------------------------------------------------------------------- + + for ( Integer orgunitId : orgunitIds ) + { + String dataValueSql = "select * from datavalue where dataelementid=" + dataelementId + + " and categoryoptioncomboid=" + optionComboId + " and sourceid=" + orgunitId + + " and periodid=" + period.getId() + ""; + + boolean hasValue = jdbcTemplate.queryForRowSet( dataValueSql ).next(); + + Double resultValue = getAggregateValue( caseExpression, caseOperator, deType, deSumId, orgunitId, + period ); + + if ( resultValue != null && resultValue != 0 ) + { + // ----------------------------------------------------- + // Add dataValue + // ----------------------------------------------------- + + if ( !hasValue ) + { + String insertValueSql = "INSERT INTO datavalue ( dataelementid, periodid, sourceid, categoryoptioncomboid, value, storedby, lastupdated, followup ) " + + "VALUES ( " + + dataelementId + + ", " + + period.getId() + + ", " + + orgunitId + + ", " + + optionComboId + + ", " + + resultValue + + ", '" + + STORED_BY_DHIS_SYSTEM + + "', '" + + DateUtils.getMediumDateString( new Date() ) + "', false )"; + jdbcTemplate.execute( insertValueSql ); + } + + // ----------------------------------------------------- + // Update dataValue + // ----------------------------------------------------- + else + { + sql = "UPDATE datavalue" + " SET value='" + resultValue + "',lastupdated='" + new Date() + + "' where dataelementId=" + dataelementId + " and periodid=" + period.getId() + + " and sourceid=" + orgunitId + " and categoryoptioncomboid=" + optionComboId + + " and storedby='" + STORED_BY_DHIS_SYSTEM + "'"; + jdbcTemplate.execute( sql ); + } + } + + // --------------------------------------------------------- + // Delete dataValue + // --------------------------------------------------------- + else if ( hasValue ) + { + String deleteSql = "DELETE from datavalue where dataelementid=dataelementid and periodid=periodid and sourceid=sourceid and categoryoptioncomboid=categoryoptioncomboid"; + jdbcTemplate.execute( deleteSql ); + } + } + } + + } + } + + private Collection getPeriods( String periodTypeName, String taskStrategy ) + { + Calendar calStartDate = Calendar.getInstance(); + + if ( TASK_AGGREGATE_QUERY_BUILDER_LAST_MONTH.equals( taskStrategy ) ) + { + calStartDate.add( Calendar.MONTH, -1 ); + } + else if ( TASK_AGGREGATE_QUERY_BUILDER_LAST_3_MONTH.equals( taskStrategy ) ) + { + calStartDate.add( Calendar.MONTH, -3 ); + } + else if ( TASK_AGGREGATE_QUERY_BUILDER_LAST_6_MONTH.equals( taskStrategy ) ) + { + calStartDate.add( Calendar.MONTH, -6 ); + } + else if ( TASK_AGGREGATE_QUERY_BUILDER_LAST_12_MONTH.equals( taskStrategy ) ) + { + calStartDate.add( Calendar.MONTH, -12 ); + } + + Date startDate = calStartDate.getTime(); + + Calendar calEndDate = Calendar.getInstance(); + + Date endDate = calEndDate.getTime(); + + CalendarPeriodType periodType = (CalendarPeriodType) CalendarPeriodType.getPeriodTypeByName( periodTypeName ); + + Collection periods = periodType.generatePeriods( startDate, endDate ); + + for ( Period period : periods ) + { + Period _period = periodService.getPeriod( period.getStartDate(), period.getEndDate(), periodType ); + if ( _period == null ) + { + int id = periodService.addPeriod( period ); + period.setId( id ); + } + else + { + period.setId( _period.getId() ); + } + } + + return periods; + } + + private String createSQL( String aggregationExpression, String operator, String deType, int orgunitId, + String startDate, String endDate ) + { + // --------------------------------------------------------------------- + // get operators + // --------------------------------------------------------------------- + + Pattern patternOperator = Pattern.compile( "(AND|OR)" ); + + Matcher matcherOperator = patternOperator.matcher( aggregationExpression ); + + List operators = new ArrayList(); + + while ( matcherOperator.find() ) + { + operators.add( matcherOperator.group() ); + } + + String[] expression = aggregationExpression.split( "(AND|OR)" ); + + // --------------------------------------------------------------------- + // parse expressions + // --------------------------------------------------------------------- + + Pattern patternCondition = Pattern.compile( regExp ); + + List conditions = new ArrayList(); + double value = 0.0; + + for ( int i = 0; i < expression.length; i++ ) + { + String subExp = expression[i]; + List subConditions = new ArrayList(); + + Matcher matcherCondition = patternCondition.matcher( expression[i] ); + + String condition = ""; + + while ( matcherCondition.find() ) + { + String match = matcherCondition.group(); + subExp = subExp.replace( match, "~" ); + match = match.replaceAll( "[\\[\\]]", "" ); + + String[] info = match.split( SEPARATOR_OBJECT ); + + if ( info[0].equalsIgnoreCase( OBJECT_PATIENT ) ) + { + condition = getConditionForPatient( orgunitId, operator, startDate, endDate ); + } + else if ( info[0].equalsIgnoreCase( OBJECT_PATIENT_PROPERTY ) ) + { + String propertyName = info[1]; + condition = getConditionForPatientProperty( propertyName, operator, startDate, endDate ); + + } + else if ( info[0].equalsIgnoreCase( OBJECT_PATIENT_ATTRIBUTE ) ) + { + int attributeId = Integer.parseInt( info[1] ); + condition = getConditionForPatientAttribute( attributeId, operator ); + } + else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM_STAGE_DATAELEMENT ) ) + { + String[] ids = info[1].split( SEPARATOR_ID ); + + int programId = Integer.parseInt( ids[0] ); + String programStageId = ids[1]; + int dataElementId = Integer.parseInt( ids[2] ); + + String valueToCompare = expression[i].replace( "[" + match + "]", "" ).trim(); + + if ( valueToCompare.equalsIgnoreCase( IS_NULL ) ) + { + condition = getConditionForNotDataElement( programId, programStageId, operator, dataElementId, + orgunitId, startDate, endDate ); + + expression[i] = expression[i].replace( valueToCompare, "" ); + } + else + { + condition = getConditionForDataElement( programId, programStageId, operator, dataElementId, + orgunitId, startDate, endDate ); + + if ( !expression[i].contains( "+" ) ) + { + if ( deType.equals( DataElement.VALUE_TYPE_INT ) ) + { + condition += " AND cast( pd.value as " + statementBuilder.getDoubleColumnType() + ") "; + } + else + { + condition += " AND pd.value "; + } + } + else + { + subConditions.add( condition ); + } + } + } + + else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM_PROPERTY ) ) + { + condition = getConditionForProgramProperty( operator, startDate, endDate, info[1] ); + } + else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM ) ) + { + String[] ids = info[1].split( SEPARATOR_ID ); + condition = getConditionForProgram( ids[0], operator, orgunitId, startDate, endDate ); + if ( ids.length > 1 ) + { + condition += ids[1]; + } + } + else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM_STAGE ) ) + { + String[] ids = info[1].split( SEPARATOR_ID ); + if ( ids.length == 2 && ids[1].equals( IN_CONDITION_COUNT_X_TIMES ) ) + { + condition = getConditionForCountProgramStage( ids[0], operator, orgunitId, startDate, endDate ); + } + else + { + condition = getConditionForProgramStage( ids[0], operator, orgunitId, startDate, endDate ); + } + } + else if ( info[0].equalsIgnoreCase( OBJECT_PROGRAM_STAGE_PROPERTY ) ) + { + condition = getConditionForProgramStageProperty( info[1], operator, orgunitId, startDate, endDate ); + } + else if ( info[0].equalsIgnoreCase( OBJECT_PATIENT_PROGRAM_STAGE_PROPERTY ) ) + { + condition = getConditionForPatientProgramStageProperty( info[1], operator, startDate, endDate ); + } + + // ------------------------------------------------------------- + // Replacing the operand with 1 in order to later be able to + // verify + // that the formula is mathematically valid + // ------------------------------------------------------------- + + if ( expression[i].contains( "+" ) ) + { + Collection patientIds = executeSQL( condition ); + value = patientIds.size(); + + subExp = subExp.replace( "~", value + "" ); + } + + condition = expression[i].replace( match, condition ).replaceAll( "[\\[\\]]", "" ); + } + + if ( expression[i].contains( "+" ) ) + { + final JEP parser = new JEP(); + + parser.parseExpression( subExp ); + + String _subExp = (parser.getValue() == 1.0) ? " AND 1 = 1 " : " AND 0 = 1 "; + + int noPlus = expression[i].split( "\\+" ).length - 1; + List subOperators = new ArrayList(); + for ( int j = 0; j < noPlus; j++ ) + { + subOperators.add( "AND" ); + } + + condition = getSQL( operator, subConditions, subOperators ) + _subExp; + } + + conditions.add( condition ); + } + + return getSQL( operator, conditions, operators ); + } + + private String getConditionForNotDataElement( int programId, String programStageId, String operator, + int dataElementId, int orgunitId, String startDate, String endDate ) + { + String sql = "SELECT distinct(pi.patientid) "; + String from = "FROM programstageinstance as psi " + + "INNER JOIN programinstance as pi ON pi.programinstanceid = psi.programinstanceid "; + + String condition = "pi.patientid "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + sql = "SELECT psi.programstageinstanceid "; + condition = "psi.programstageinstanceid "; + } + + sql += from + + "LEFT OUTER JOIN patientdatavalue as pd ON psi.programstageinstanceid = pd.programstageinstanceid " + + "WHERE psi.executionDate >= '" + startDate + "' AND psi.executionDate <= '" + endDate + "' " + + "AND pd.value IS NULL AND " + condition + " NOT IN ( " + "SELECT " + condition + from + + "WHERE psi.organisationunitid = " + orgunitId + " AND pi.programid = " + programId + " " + + "AND psi.executionDate >= '" + startDate + "' AND psi.executionDate <= '" + endDate + "' " + + "AND pd.dataelementid = " + dataElementId + " "; + + if ( !programStageId.equals( IN_CONDITION_GET_ALL ) ) + { + sql += " AND psi.programstageid = " + programStageId; + } + + return sql + " ) "; + } + + private String getConditionForDataElement( int programId, String programStageId, String operator, + int dataElementId, int orgunitId, String startDate, String endDate ) + { + String sql = "SELECT distinct(pi.patientid) "; + String from = "FROM programstageinstance as psi " + + "INNER JOIN patientdatavalue as pd ON psi.programstageinstanceid = pd.programstageinstanceid " + + "INNER JOIN programinstance as pi ON pi.programinstanceid = psi.programinstanceid "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + sql = "SELECT psi.programstageinstanceid "; + from = "FROM programstageinstance as psi " + + "INNER JOIN patientdatavalue as pd ON psi.programstageinstanceid = pd.programstageinstanceid "; + } + + sql += from + " WHERE pd.dataelementid=" + dataElementId + " AND psi.organisationunitid=" + orgunitId + + " AND psi.executionDate>='" + startDate + "' AND psi.executionDate <= '" + endDate + "'"; + + if ( !programStageId.equals( IN_CONDITION_GET_ALL ) ) + { + sql += " AND psi.programstageid = " + programStageId; + } + + return sql; + } + + private String getConditionForPatientAttribute( int attributeId, String operator ) + { + String sql = "SELECT distinct(pi.patientid) "; + String from = "FROM patientattributevalue pi "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + sql = "SELECT psi.programstageinstanceid "; + from = "FROM programstageinstance psi inner join programinstance pi " + + "on psi.programinstanceid=pi.programinstanceid " + "inner join patientattributevalue pav " + + "on pav.patientid=pi.patientid "; + } + + return sql + from + "WHERE patientattributeid=" + attributeId + " AND value "; + } + + private String getConditionForPatient( int orgunitId, String operator, String startDate, String endDate ) + { + String sql = "SELECT pi.patientid "; + String from = "FROM patient pi "; + String where = "WHERE pi.organisationunitid=" + orgunitId + " AND pi.registrationdate>= '" + startDate + "' " + + "AND pi.registrationdate <= '" + endDate + "'"; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + sql = "SELECT psi.programstageinstanceid "; + from = "FROM programstageinstance psi inner join programinstance pi " + + "on psi.programinstanceid=pi.programinstanceid " + + "inner join patient p on p.patientid=pi.patientid "; + where = "WHERE p.organisationunitid=" + orgunitId + " AND p.registrationdate>= '" + startDate + "' " + + "AND p.registrationdate <= '" + endDate + "'"; + } + + return sql + from + where; + } + + private String getConditionForPatientProperty( String propertyName, String operator, String startDate, + String endDate ) + { + String sql = "SELECT distinct(pi.patientid) FROM patient pi WHERE "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + sql = "SELECT psi.programstageinstanceid " + "FROM programstageinstance psi inner join programinstance pi " + + "on psi.programinstanceid=pi.programinstanceid " + + "inner join patient p on p.patientid=pi.patientid WHERE "; + } + + if ( propertyName.equals( PROPERTY_AGE ) ) + { + sql += "DATE(registrationdate) - DATE(birthdate) "; + } + else + { + sql += propertyName + " "; + } + + return sql; + } + + private String getConditionForPatientProgramStageProperty( String propertyName, String operator, String startDate, + String endDate ) + { + String sql = "SELECT distinct(pi.patientid) "; + String from = "FROM programinstance pi INNER JOIN programstageinstance psi " + + "ON psi.programinstanceid=pi.programinstanceid "; + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + sql = "SELECT psi.programstageinstance "; + from = "FROM programstageinstance psi "; + } + + from += "inner join patient p on p.patientid=pi.patientid "; + + sql += from + "WHERE executionDate>='" + startDate + "' and executionDate<='" + endDate + "' and " + + propertyName; + + return sql; + } + + private String getConditionForProgramProperty( String operator, String startDate, String endDate, String property ) + { + String sql = "SELECT pi.patientid FROM programinstance as pi "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + sql = "SELECT psi.programstageinstanceid FROM programinstance as pi " + + "INNER JOIN programstageinstance psi ON psi.programinstanceid=pi.programinstanceid "; + } + + return sql + "WHERE pi.enrollmentdate>='" + startDate + "' " + "AND pi.enrollmentdate<='" + endDate + "' AND " + + property; + } + + private String getConditionForProgram( String programId, String operator, int orgunitId, String startDate, + String endDate ) + { + String sql = "SELECT distinct(pi.patientid) FROM programinstance as pi " + + "inner join patient psi on psi.patientid=pi.patientid "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + sql = "SELECT psi.programstageinstanceid FROM programinstance as pi " + + "INNER JOIN programstageinstance psi ON pi.programinstanceid=psi.programinstanceid "; + } + + return sql + "WHERE pi.programid=" + programId + " " + " AND psi.organisationunitid = " + orgunitId + + " AND pi.enrollmentdate >= '" + startDate + "' AND pi.enrollmentdate <= '" + endDate + "' "; + } + + private String getConditionForProgramStage( String programStageId, String operator, int orgunitId, + String startDate, String endDate ) + { + String select = "SELECT distinct(pi.patientid) "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + select = "SELECT psi.programstageinstanceid "; + } + + return select + "FROM programinstance as pi INNER JOIN programstageinstance psi " + + "ON pi.programinstanceid = psi.programinstanceid WHERE psi.programstageid=" + programStageId + " " + + "AND psi.executiondate >= '" + startDate + "' AND psi.executiondate <= '" + endDate + + "' AND psi.organisationunitid = " + orgunitId + " "; + } + + private String getConditionForCountProgramStage( String programStageId, String operator, int orgunitId, + String startDate, String endDate ) + { + String select = "SELECT distinct(pi.patientid) "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + select = "SELECT psi.programstageinstanceid "; + } + + select += "FROM programstageinstance as psi " + + "INNER JOIN programinstance as pi ON pi.programinstanceid = psi.programinstanceid " + + "WHERE psi.organisationunitid = " + orgunitId + " and psi.programstageid = " + programStageId + " " + + "AND psi.executionDate >= '" + startDate + "' AND psi.executionDate <= '" + endDate + "' " + + "GROUP BY psi.programinstanceid "; + + if ( operator.equals( AGGRERATION_COUNT ) ) + { + select += ",pi.patientid "; + } + + select += "HAVING count(psi.programstageinstanceid) "; + + return select; + + } + + private String getConditionForProgramStageProperty( String property, String operator, int orgunitId, + String startDate, String endDate ) + { + String select = "SELECT distinct(pi.patientid) "; + + if ( !operator.equals( AGGRERATION_COUNT ) ) + { + select = "SELECT psi.programstageinstanceid "; + } + + return select + "FROM programinstance as pi INNER JOIN programstageinstance psi " + + "ON pi.programinstanceid = psi.programinstanceid WHERE " + " psi.executiondate >= '" + startDate + + "' AND psi.executiondate <= '" + endDate + "' AND psi.organisationunitid = " + orgunitId + " AND " + + property; + } + + private String getSQL( String aggregateOperator, List conditions, List operators ) + { + String sql = conditions.get( 0 ); + + String sqlAnd = ""; + + int index = 0; + + for ( index = 0; index < operators.size(); index++ ) + { + if ( operators.get( index ).equalsIgnoreCase( OPERATOR_AND ) ) + { + if ( aggregateOperator.equals( AGGRERATION_COUNT ) ) + { + sql += " AND pi.patientid IN ( " + conditions.get( index + 1 ); + } + else + { + sql += " AND psi.programstageinstanceid IN ( " + conditions.get( index + 1 ); + } + sqlAnd += ")"; + } + else + { + sql += sqlAnd; + sql += " UNION ( " + conditions.get( index + 1 ) + " ) "; + sqlAnd = ""; + } + } + + sql += sqlAnd; + + return sql; + } + } === modified file 'dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/patient/scheduling/CaseAggregateConditionTask.java' --- dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/patient/scheduling/CaseAggregateConditionTask.java 2013-03-29 04:26:45 +0000 +++ dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/patient/scheduling/CaseAggregateConditionTask.java 2013-04-01 05:56:41 +0000 @@ -27,31 +27,15 @@ package org.hisp.dhis.patient.scheduling; -import static org.hisp.dhis.patient.scheduling.CaseAggregateConditionSchedulingManager.TASK_AGGREGATE_QUERY_BUILDER_LAST_12_MONTH; -import static org.hisp.dhis.patient.scheduling.CaseAggregateConditionSchedulingManager.TASK_AGGREGATE_QUERY_BUILDER_LAST_3_MONTH; -import static org.hisp.dhis.patient.scheduling.CaseAggregateConditionSchedulingManager.TASK_AGGREGATE_QUERY_BUILDER_LAST_6_MONTH; -import static org.hisp.dhis.patient.scheduling.CaseAggregateConditionSchedulingManager.TASK_AGGREGATE_QUERY_BUILDER_LAST_MONTH; import static org.hisp.dhis.setting.SystemSettingManager.DEFAULT_SCHEDULE_AGGREGATE_QUERY_BUILDER_TASK_STRATEGY; import static org.hisp.dhis.setting.SystemSettingManager.KEY_SCHEDULE_AGGREGATE_QUERY_BUILDER_TASK_STRATEGY; import static org.hisp.dhis.system.notification.NotificationLevel.INFO; -import java.util.Calendar; -import java.util.Collection; -import java.util.Date; +import java.util.ArrayList; +import java.util.List; -import org.hisp.dhis.caseaggregation.CaseAggregationCondition; +import org.hisp.dhis.caseaggregation.CaseAggregateSchedule; import org.hisp.dhis.caseaggregation.CaseAggregationConditionService; -import org.hisp.dhis.dataelement.DataElement; -import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo; -import org.hisp.dhis.dataelement.DataElementCategoryService; -import org.hisp.dhis.dataelement.DataElementService; -import org.hisp.dhis.datavalue.DataValue; -import org.hisp.dhis.datavalue.DataValueService; -import org.hisp.dhis.organisationunit.OrganisationUnit; -import org.hisp.dhis.organisationunit.OrganisationUnitService; -import org.hisp.dhis.period.CalendarPeriodType; -import org.hisp.dhis.period.Period; -import org.hisp.dhis.program.ProgramStageInstanceService; import org.hisp.dhis.scheduling.TaskId; import org.hisp.dhis.setting.SystemSettingManager; import org.hisp.dhis.system.notification.Notifier; @@ -68,55 +52,31 @@ public class CaseAggregateConditionTask implements Runnable { - public static final String STORED_BY_DHIS_SYSTEM = "DHIS-System"; - private CaseAggregationConditionService aggregationConditionService; - - private DataValueService dataValueService; - + private JdbcTemplate jdbcTemplate; - - private DataElementService dataElementService; - - private DataElementCategoryService categoryService; - + private SystemSettingManager systemSettingManager; - - private ProgramStageInstanceService programStageInstanceService; - - private OrganisationUnitService organisationUnitService; private Notifier notifier; - public void setNotifier( Notifier notifier ) - { - this.notifier = notifier; - } - private TaskId taskId; public void setTaskId( TaskId taskId ) { this.taskId = taskId; } - + // ------------------------------------------------------------------------- // Constructors // ------------------------------------------------------------------------- public CaseAggregateConditionTask( CaseAggregationConditionService aggregationConditionService, - DataValueService dataValueService, JdbcTemplate jdbcTemplate, DataElementService dataElementService, - DataElementCategoryService categoryService, SystemSettingManager systemSettingManager, - ProgramStageInstanceService programStageInstanceService, OrganisationUnitService organisationUnitService, Notifier notifier ) + JdbcTemplate jdbcTemplate, SystemSettingManager systemSettingManager, Notifier notifier ) { this.aggregationConditionService = aggregationConditionService; - this.dataValueService = dataValueService; this.jdbcTemplate = jdbcTemplate; - this.dataElementService = dataElementService; - this.categoryService = categoryService; this.systemSettingManager = systemSettingManager; - this.programStageInstanceService = programStageInstanceService; - this.organisationUnitService = organisationUnitService; this.notifier = notifier; } @@ -128,146 +88,35 @@ public void run() { final int cpuCores = SystemUtils.getCpuCores(); - Clock clock = new Clock().startClock().logTime( "Aggregate process started, number of CPU cores: " + cpuCores + ", " + SystemUtils.getMemoryString() ); + Clock clock = new Clock().startClock().logTime( + "Aggregate process started, number of CPU cores: " + cpuCores + ", " + SystemUtils.getMemoryString() ); notifier.clear( taskId ).notify( taskId, "Aggregate process started" ); - + String taskStrategy = (String) systemSettingManager.getSystemSetting( KEY_SCHEDULE_AGGREGATE_QUERY_BUILDER_TASK_STRATEGY, DEFAULT_SCHEDULE_AGGREGATE_QUERY_BUILDER_TASK_STRATEGY ); - String datasetSQL = "select dm.datasetid as datasetid, pt.name as periodname, ds.name as datasetname"; + // Get datasets which are used in case-aggregate-query-builder formula + + String datasetSQL = "select dm.datasetid as datasetid, pt.name as periodtypename, ds.name as datasetname"; datasetSQL += " from caseaggregationcondition cagg inner join datasetmembers dm "; datasetSQL += " on cagg.aggregationdataelementid=dm.dataelementid inner join dataset ds "; datasetSQL += " on ds.datasetid = dm.datasetid inner join periodtype pt "; datasetSQL += " on pt.periodtypeid=ds.periodtypeid "; SqlRowSet rsDataset = jdbcTemplate.queryForRowSet( datasetSQL ); + List caseAggregateSchedule = new ArrayList(); while ( rsDataset.next() ) { - int datasetId = rsDataset.getInt( "datasetid" ); - String datasetName = rsDataset.getString( "datasetname" ); - - Collection periods = getPeriod( rsDataset.getString( "periodname" ), taskStrategy ); - - for ( Period period : periods ) - { - String sql = "select caseaggregationconditionid, aggregationdataelementid, optioncomboid " - + " from caseaggregationcondition cagg inner join datasetmembers dm " - + " on cagg.aggregationdataelementid=dm.dataelementid " + "inner join dataset ds " - + " on ds.datasetid = dm.datasetid " + "inner join periodtype pt " - + " on pt.periodtypeid=ds.periodtypeid " + "where ds.datasetid = " + datasetId; - - SqlRowSet rs = jdbcTemplate.queryForRowSet( sql ); - - Collection orgunitIds = programStageInstanceService.getOrganisationUnitIds( - period.getStartDate(), period.getEndDate() ); - - while ( rs.next() ) - { - // ------------------------------------------------------------- - // Get formula, agg-dataelement and option-combo - // ------------------------------------------------------------- - - int dataelementId = rs.getInt( "aggregationdataelementid" ); - int optionComboId = rs.getInt( "optioncomboid" ); - - DataElement dElement = dataElementService.getDataElement( dataelementId ); - DataElementCategoryOptionCombo optionCombo = categoryService - .getDataElementCategoryOptionCombo( optionComboId ); - - CaseAggregationCondition aggCondition = aggregationConditionService.getCaseAggregationCondition( rs - .getInt( "caseaggregationconditionid" ) ); - - // --------------------------------------------------------------------- - // Aggregation - // --------------------------------------------------------------------- - - for ( Integer orgUnitId : orgunitIds ) - { - OrganisationUnit orgUnit = organisationUnitService.getOrganisationUnit( orgUnitId ); - DataValue dataValue = dataValueService.getDataValue( orgUnit, dElement, period, optionCombo ); - - Integer resultValue = aggregationConditionService.parseConditition( aggCondition, orgUnit, - period ); - - if ( resultValue != null && resultValue != 0 ) - { - // ----------------------------------------------------- - // Add dataValue - // ----------------------------------------------------- - - if ( dataValue == null ) - { - dataValue = new DataValue( dElement, period, orgUnit, "" + resultValue, STORED_BY_DHIS_SYSTEM, new Date(), - null, optionCombo ); - dataValueService.addDataValue( dataValue ); - } - - // ----------------------------------------------------- - // Update dataValue - // ----------------------------------------------------- - else if ( (double) resultValue != Double.parseDouble( dataValue.getValue() ) ) - { - dataValue.setValue( "" + resultValue ); - dataValue.setTimestamp( new Date() ); - sql = "UPDATE datavalue" + " SET value='" + resultValue + "',lastupdated='" - + new Date() + "' where dataelementId=" + dataelementId + " and periodid=" - + period.getId() + " and sourceid=" + orgUnit.getId() - + " and categoryoptioncomboid=" + optionComboId + " and storedby='" - + STORED_BY_DHIS_SYSTEM + "'"; - jdbcTemplate.execute( sql ); - } - } - - // --------------------------------------------------------- - // Delete dataValue - // --------------------------------------------------------- - else if ( dataValue != null ) - { - dataValueService.deleteDataValue( dataValue ); - } - } - } - - } - clock.logTime( "Improrted aggregate data completed for data set " + datasetName ); - notifier.notify( taskId, "Improrted aggregate data completed for data set " + datasetName ); + CaseAggregateSchedule dataSet = new CaseAggregateSchedule( rsDataset.getInt( "datasetid" ), + rsDataset.getString( "datasetname" ), rsDataset.getString( "periodtypename" ) ); + caseAggregateSchedule.add( dataSet ); } + + aggregationConditionService.aggregate( caseAggregateSchedule, taskStrategy ); + + clock.logTime( "Improrted aggregate data completed " ); + notifier.notify( taskId, INFO, "Improrted aggregate data completed", true ); } - - // ------------------------------------------------------------------------- - // Supportive methods - // ------------------------------------------------------------------------- - - private Collection getPeriod( String periodTypeName, String taskStrategy ) - { - Calendar calStartDate = Calendar.getInstance(); - - if ( TASK_AGGREGATE_QUERY_BUILDER_LAST_MONTH.equals( taskStrategy ) ) - { - calStartDate.add( Calendar.MONTH, -1 ); - } - else if ( TASK_AGGREGATE_QUERY_BUILDER_LAST_3_MONTH.equals( taskStrategy ) ) - { - calStartDate.add( Calendar.MONTH, -3 ); - } - else if ( TASK_AGGREGATE_QUERY_BUILDER_LAST_6_MONTH.equals( taskStrategy ) ) - { - calStartDate.add( Calendar.MONTH, -6 ); - } - else if ( TASK_AGGREGATE_QUERY_BUILDER_LAST_12_MONTH.equals( taskStrategy ) ) - { - calStartDate.add( Calendar.MONTH, -12 ); - } - - Date startDate = calStartDate.getTime(); - - Calendar calEndDate = Calendar.getInstance(); - - Date endDate = calEndDate.getTime(); - - CalendarPeriodType periodType = (CalendarPeriodType) CalendarPeriodType.getPeriodTypeByName( periodTypeName ); - - return periodType.generatePeriods( startDate, endDate ); - } + } === modified file 'dhis-2/dhis-services/dhis-service-patient/src/main/resources/META-INF/dhis/beans.xml' --- dhis-2/dhis-services/dhis-service-patient/src/main/resources/META-INF/dhis/beans.xml 2013-03-29 04:26:45 +0000 +++ dhis-2/dhis-services/dhis-service-patient/src/main/resources/META-INF/dhis/beans.xml 2013-04-01 05:56:41 +0000 @@ -12,6 +12,8 @@ value="org.hisp.dhis.caseaggregation.CaseAggregationCondition" /> + + - @@ -482,12 +483,7 @@ - - - - - === modified file 'dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseaggregation/CaseAggregationResultAction.java' --- dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseaggregation/CaseAggregationResultAction.java 2013-03-25 04:02:33 +0000 +++ dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseaggregation/CaseAggregationResultAction.java 2013-04-01 05:56:41 +0000 @@ -210,7 +210,6 @@ public String execute() throws Exception { - // --------------------------------------------------------------------- // Get CaseAggregateCondition list // --------------------------------------------------------------------- @@ -277,7 +276,7 @@ for ( Period period : periods ) { - Integer resultValue = aggregationConditionService.parseConditition( condition, orgUnit, period ); + Double resultValue = aggregationConditionService.getAggregateValue( condition, orgUnit, period ); DataValue dataValue = dataValueService.getDataValue( orgUnit, dElement, period, optionCombo ); String key = orgUnitId + "-" + format.formatPeriod( period );