dhis2-devs team mailing list archive
-
dhis2-devs team
-
Mailing list archive
-
Message #33705
[Branch ~dhis2-devs-core/dhis2/trunk] Rev 17257: Approvals performance improvement
------------------------------------------------------------
revno: 17257
committer: jimgrace@xxxxxxxxx
branch nick: dhis2
timestamp: Fri 2014-10-24 06:01:14 -0400
message:
Approvals performance improvement
modified:
dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissions.java
dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalService.java
dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalStore.java
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java
dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java
dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml
dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java
--
lp:dhis2
https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk
Your team DHIS 2 developers is subscribed to branch lp:dhis2.
To unsubscribe from this branch go to https://code.launchpad.net/~dhis2-devs-core/dhis2/trunk/+edit-subscription
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissions.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissions.java 2014-10-23 18:10:58 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalPermissions.java 2014-10-24 10:01:14 +0000
@@ -43,6 +43,8 @@
private boolean mayAccept;
private boolean mayUnaccept;
+
+ private boolean mayReadData;
private transient String state;
@@ -95,6 +97,17 @@
}
@JsonProperty
+ public boolean isMayReadData()
+ {
+ return mayReadData;
+ }
+
+ public void setMayReadData( boolean mayReadData )
+ {
+ this.mayReadData = mayReadData;
+ }
+
+ @JsonProperty
public String getState()
{
return state;
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalService.java 2014-10-23 21:47:57 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalService.java 2014-10-24 10:01:14 +0000
@@ -109,8 +109,8 @@
* category option combos that the user is allowed to see.
*
* @param dataSets DataSets that we are getting the status for
- * @param period period we are getting the status for
+ * @param periods Periods we are getting the status for
* @return list of status and permissions
*/
- List<DataApprovalStatus> getUserDataApprovalsAndPermissions( Set<DataSet> dataSets, Period period );
+ List<DataApprovalStatus> getUserDataApprovalsAndPermissions( Set<DataSet> dataSets, Set<Period> periods );
}
=== modified file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalStore.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalStore.java 2014-10-23 10:07:41 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/dataapproval/DataApprovalStore.java 2014-10-24 10:01:14 +0000
@@ -33,6 +33,8 @@
import org.hisp.dhis.organisationunit.OrganisationUnit;
import org.hisp.dhis.period.Period;
+import java.util.Set;
+
/**
* Defines the functionality for persisting DataApproval objects.
*
@@ -81,4 +83,16 @@
*/
DataApproval getDataApproval( DataApprovalLevel dataApprovalLevel, DataSet dataSet, Period period,
OrganisationUnit organisationUnit, DataElementCategoryOptionCombo attributeOptionCombo );
+
+ /**
+ * Returns a set of DataApproval objects representing the approval states
+ * for organisation units & category option combos that the user is allowed
+ * to see.
+ *
+ * @param dataSets Data sets to look within
+ * @param periods Periods to look within
+ * @return data approval objects for the user to see
+ */
+ Set<DataApproval> getUserDataApprovals( Set<DataSet> dataSets, Set<Period> periods);
+
}
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java 2014-10-23 21:47:57 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/DefaultDataApprovalService.java 2014-10-24 10:01:14 +0000
@@ -366,7 +366,57 @@
}
@Override
- public List<DataApprovalStatus> getUserDataApprovalsAndPermissions( Set<DataSet> dataSets, Period period )
+ public List<DataApprovalStatus> getUserDataApprovalsAndPermissions( Set<DataSet> dataSets, Set<Period> periods )
+ {
+ User user = currentUserService.getCurrentUser();
+
+ boolean authorizedToApprove = user.getUserCredentials().isAuthorized( DataApproval.AUTH_APPROVE );
+ boolean authorizedToApproveAtLowerLevels = user.getUserCredentials().isAuthorized( DataApproval.AUTH_APPROVE_LOWER_LEVELS );
+ boolean authorizedToAcceptAtLowerLevels = user.getUserCredentials().isAuthorized( DataApproval.AUTH_ACCEPT_LOWER_LEVELS );
+
+ int maxApprovalLevel = dataApprovalLevelService.getAllDataApprovalLevels().size();
+
+ List<DataApprovalStatus> statusList = new ArrayList<>();
+
+ for ( DataApproval da : dataApprovalStore.getUserDataApprovals( dataSets, periods ) )
+ {
+ DataApprovalPermissions permissions = new DataApprovalPermissions();
+
+ if ( da.getOrganisationUnit() != null ) //TODO: Shouldn't be null -- fix the category option mappings to org units in the database.
+ {
+ DataApprovalLevel userApprovalLevel = dataApprovalLevelService.getUserApprovalLevel( da.getOrganisationUnit(), false );
+
+ if ( userApprovalLevel != null )
+ {
+ int userLevel = userApprovalLevel.getLevel();
+ int dataLevel = da.getDataApprovalLevel() == null ? maxApprovalLevel + 1 : da.getDataApprovalLevel().getLevel();
+
+ boolean mayApprove = ( authorizedToApprove && userLevel == dataLevel && !da.isAccepted() ) ||
+ authorizedToApproveAtLowerLevels && userLevel < dataLevel;
+
+ boolean mayAcceptOrUnaccept = authorizedToAcceptAtLowerLevels && dataLevel <= maxApprovalLevel &&
+ ( userLevel == dataLevel + 1 || ( userLevel < dataLevel && authorizedToApproveAtLowerLevels ) );
+
+ boolean mayUnapprove = mayApprove && ( !da.isAccepted() || mayAcceptOrUnaccept );
+
+ permissions.setMayApprove( mayApprove );
+ permissions.setMayUnapprove( mayUnapprove );
+ permissions.setMayAccept( mayAcceptOrUnaccept );
+ permissions.setMayUnaccept( mayAcceptOrUnaccept );
+ }
+ boolean mayReadData = true; //TODO: Fix.
+
+ permissions.setMayReadData( mayReadData );
+
+ statusList.add( new DataApprovalStatus( null, da, da.getDataApprovalLevel(), permissions ) );
+ }
+ }
+
+ return statusList;
+ }
+
+ /*
+ public List<DataApprovalStatus> getUserDataApprovalsAndPermissions( Set<DataSet> dataSets, Set<Period> periods )
{
tracePrint( "---------------------------------------------------------------------- getUserDataApprovalsAndPermissions" );
@@ -390,6 +440,7 @@
return statusList;
}
+ */
// -------------------------------------------------------------------------
// Supportive methods
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java'
--- dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java 2014-10-21 19:45:22 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/java/org/hisp/dhis/dataapproval/hibernate/HibernateDataApprovalStore.java 2014-10-24 10:01:14 +0000
@@ -32,13 +32,29 @@
import org.hibernate.criterion.Restrictions;
import org.hisp.dhis.dataapproval.DataApproval;
import org.hisp.dhis.dataapproval.DataApprovalLevel;
+import org.hisp.dhis.dataapproval.DataApprovalLevelService;
import org.hisp.dhis.dataapproval.DataApprovalStore;
+import org.hisp.dhis.dataelement.DataElementCategoryCombo;
import org.hisp.dhis.dataelement.DataElementCategoryOptionCombo;
+import org.hisp.dhis.dataelement.DataElementCategoryService;
import org.hisp.dhis.dataset.DataSet;
import org.hisp.dhis.hibernate.HibernateGenericStore;
import org.hisp.dhis.organisationunit.OrganisationUnit;
+import org.hisp.dhis.organisationunit.OrganisationUnitService;
import org.hisp.dhis.period.Period;
import org.hisp.dhis.period.PeriodService;
+import org.hisp.dhis.system.util.ConversionUtils;
+import org.hisp.dhis.system.util.DateUtils;
+import org.hisp.dhis.system.util.MathUtils;
+import org.hisp.dhis.system.util.TextUtils;
+import org.hisp.dhis.user.CurrentUserService;
+import org.hisp.dhis.user.User;
+import org.springframework.jdbc.core.JdbcTemplate;
+import org.springframework.jdbc.support.rowset.SqlRowSet;
+
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Set;
/**
* @author Jim Grace
@@ -51,6 +67,13 @@
// Dependencies
// -------------------------------------------------------------------------
+ private JdbcTemplate jdbcTemplate;
+
+ public void setJdbcTemplate( JdbcTemplate jdbcTemplate )
+ {
+ this.jdbcTemplate = jdbcTemplate;
+ }
+
private PeriodService periodService;
public void setPeriodService( PeriodService periodService )
@@ -58,6 +81,34 @@
this.periodService = periodService;
}
+ private CurrentUserService currentUserService;
+
+ public void setCurrentUserService( CurrentUserService currentUserService )
+ {
+ this.currentUserService = currentUserService;
+ }
+
+ private OrganisationUnitService organisationUnitService;
+
+ public void setOrganisationUnitService( OrganisationUnitService organisationUnitService )
+ {
+ this.organisationUnitService = organisationUnitService;
+ }
+
+ private DataElementCategoryService categoryService;
+
+ public void setCategoryService( DataElementCategoryService categoryService )
+ {
+ this.categoryService = categoryService;
+ }
+
+ private DataApprovalLevelService dataApprovalLevelService;
+
+ public void setDataApprovalLevelService( DataApprovalLevelService dataApprovalLevelService )
+ {
+ this.dataApprovalLevelService = dataApprovalLevelService;
+ }
+
// -------------------------------------------------------------------------
// DataApproval
// -------------------------------------------------------------------------
@@ -101,4 +152,175 @@
return (DataApproval) criteria.uniqueResult();
}
+
+ @Override
+ public Set<DataApproval> getUserDataApprovals( Set<DataSet> dataSets, Set<Period> periods)
+ {
+ Date minDate = null;
+ Date maxDate = null;
+
+ for ( Period p : periods )
+ {
+ if ( minDate == null || p.getStartDate().before( minDate ) )
+ {
+ minDate = p.getStartDate();
+ }
+ if ( maxDate == null || p.getEndDate().after( maxDate ) )
+ {
+ maxDate = p.getEndDate();
+ }
+ }
+
+ String sPeriods = "";
+
+ for ( Period p : periods )
+ {
+ sPeriods += ( sPeriods.isEmpty() ? "" : ", " ) + periodService.reloadPeriod( p ).getId();
+ }
+
+ Set<Integer> categoryComboIds = new HashSet<>();
+
+ for ( DataSet ds : dataSets )
+ {
+ categoryComboIds.add( ds.getCategoryCombo().getId() );
+ }
+
+ String sDataSetCCs = TextUtils.getCommaDelimitedString( categoryComboIds );
+
+ String limitCategoryOptionByOrgUnit = "";
+ String limitApprovalByOrgUnit = "";
+
+ for ( OrganisationUnit orgUnit : currentUserService.getCurrentUser().getOrganisationUnits() )
+ {
+ if ( orgUnit.getParent() == null ) // User has root org unit access
+ {
+ limitCategoryOptionByOrgUnit = "";
+ limitApprovalByOrgUnit = "";
+ break;
+ }
+
+ int level = organisationUnitService.getLevelOfOrganisationUnit( orgUnit );
+ limitCategoryOptionByOrgUnit += "ous.idlevel" + level + " = " + orgUnit.getId() + " or ";
+ limitApprovalByOrgUnit += "ousda.idlevel" + level + " = " + orgUnit.getId() + " or ";
+ }
+
+ if ( !limitCategoryOptionByOrgUnit.isEmpty() )
+ {
+ limitCategoryOptionByOrgUnit = "and (" + limitCategoryOptionByOrgUnit + "coo.categoryoptionid is null) ";
+ limitApprovalByOrgUnit = "and (" + limitApprovalByOrgUnit + "ousda.organisationunitid is null) ";
+ }
+
+ String limitBySharing = "";
+
+ if ( !currentUserService.currentUserIsSuper() )
+ {
+ limitBySharing = "and (ugm.userid = " + currentUserService.getCurrentUser().getId() + " or left(co.publicaccess,1) = 'r') ";
+ }
+
+ String sql = "select ccoc.categoryoptioncomboid, da.periodid, dal.level, coo.organisationunitid, da.accepted " +
+ "from categorycombos_optioncombos ccoc " +
+ "join categoryoptioncombos_categoryoptions cocco on cocco.categoryoptioncomboid = ccoc.categoryoptioncomboid " +
+ "join dataelementcategoryoption co on co.categoryoptionid = cocco.categoryoptionid " +
+ "left outer join cateogryoption_organisationunits coo on coo.categoryoptionid = cocco.categoryoptionid " +
+ "left outer join _orgunitstructure ous on ous.organisationunitid = coo.organisationunitid " +
+ "left outer join dataelementcategoryoptionusergroupaccesses couga on couga.categoryoptionid = cocco.categoryoptionid " +
+ "left outer join usergroupaccess uga on uga.usergroupaccessid = couga.usergroupaccessid " +
+ "left outer join usergroupmembers ugm on ugm.usergroupid = uga.usergroupid " +
+ "left outer join dataapproval da on da.categoryoptioncomboid = ccoc.categoryoptioncomboid and da.periodid in (" + sPeriods + ") " +
+ "left outer join dataapprovallevel dal on dal.dataapprovallevelid = da.dataapprovallevelid " +
+ "left outer join _orgunitstructure ousda on ousda.organisationunitid = da.organisationunitid " +
+ "where ccoc.categorycomboid in (" + sDataSetCCs + ") " +
+ "and (co.startdate is null or co.startdate <= '" + DateUtils.getMediumDateString( maxDate ) + "') " +
+ "and (co.enddate is null or co.enddate >= '" + DateUtils.getMediumDateString( minDate ) + "') " +
+ limitCategoryOptionByOrgUnit +
+ limitApprovalByOrgUnit +
+ limitBySharing +
+ "group by ccoc.categoryoptioncomboid, da.periodid, dal.level, coo.organisationunitid, da.accepted " +
+ "order by ccoc.categoryoptioncomboid, da.periodid, dal.level";
+
+ System.out.println( "sql = " + sql );
+ SqlRowSet rowSet = jdbcTemplate.queryForRowSet( sql );
+
+ int previousAttributeOptionComboId = 0;
+ int previousPeriodId = 0;
+ int previousLevel = 0;
+
+ Set<DataApproval> userDataApprovals = new HashSet<>();
+
+ while ( rowSet.next() )
+ {
+ Integer attributeOptionComboId = rowSet.getInt( 1 );
+ Integer periodId = rowSet.getInt( 2 );
+ Integer level = rowSet.getInt( 3 );
+ Integer orgUnitId = rowSet.getInt( 4 );
+ Boolean accepted = rowSet.getBoolean( 5 );
+
+ if ( attributeOptionComboId == previousAttributeOptionComboId && periodId == previousPeriodId && level > previousLevel )
+ {
+ continue;
+ }
+
+ previousAttributeOptionComboId = attributeOptionComboId;
+ previousPeriodId = periodId;
+ previousLevel = level;
+
+ DataElementCategoryOptionCombo attributeOptionCombo = categoryService.getDataElementCategoryOptionCombo( attributeOptionComboId );
+ Period period = periodService.getPeriod( periodId );
+ DataApprovalLevel dataApprovalLevel = ( level == null ? null : dataApprovalLevelService.getDataApprovalLevelByLevelNumber( level ) );
+ OrganisationUnit orgUnit = ( orgUnitId == null ? null : organisationUnitService.getOrganisationUnit( orgUnitId ) );
+
+ //TODO: currently special cased for PEFPAR's requirements. Can we make it more generic?
+ if ( level > 1 && attributeOptionCombo.equals ( categoryService.getDefaultDataElementCategoryOptionCombo() ) )
+ {
+ for ( OrganisationUnit ou : getUserOrgsAtLevel( 3 ) )
+ {
+ DataApproval da = new DataApproval( dataApprovalLevel, null, period, ou, attributeOptionCombo, accepted, null, null );
+
+ userDataApprovals.add( da );
+ }
+
+ continue;
+ }
+ DataApproval da = new DataApproval( dataApprovalLevel, null, period, orgUnit, attributeOptionCombo, accepted, null, null );
+
+ userDataApprovals.add( da );
+ }
+
+ return userDataApprovals;
+ }
+
+ // -------------------------------------------------------------------------
+ // Supportive methods
+ // -------------------------------------------------------------------------
+
+ private Set<OrganisationUnit> getUserOrgsAtLevel( int desiredLevel )
+ {
+ Set<OrganisationUnit> orgUnits = new HashSet<>();
+
+ for ( OrganisationUnit orgUnit : currentUserService.getCurrentUser().getOrganisationUnits() )
+ {
+ orgUnits.addAll( getOrgsAtLevel( orgUnit, desiredLevel, organisationUnitService.getLevelOfOrganisationUnit( orgUnit ) ) );
+ }
+
+ return orgUnits;
+ }
+
+ private Set<OrganisationUnit> getOrgsAtLevel( OrganisationUnit orgUnit, int desiredLevel, int thisLevel )
+ {
+ Set<OrganisationUnit> orgUnits = new HashSet<>();
+
+ if ( thisLevel < desiredLevel )
+ {
+ for ( OrganisationUnit child : orgUnit.getChildren() )
+ {
+ orgUnits.addAll( getOrgsAtLevel( child, desiredLevel, thisLevel + 1 ) );
+ }
+ }
+ else if ( thisLevel == desiredLevel )
+ {
+ orgUnits.add( orgUnit );
+ }
+
+ return orgUnits;
+ }
}
=== modified file 'dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml'
--- dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2014-10-23 10:07:41 +0000
+++ dhis-2/dhis-services/dhis-service-core/src/main/resources/META-INF/dhis/beans.xml 2014-10-24 10:01:14 +0000
@@ -71,8 +71,13 @@
<bean id="org.hisp.dhis.dataapproval.DataApprovalStore" class="org.hisp.dhis.dataapproval.hibernate.HibernateDataApprovalStore">
<property name="clazz" value="org.hisp.dhis.dataapproval.DataApproval" />
+ <property name="jdbcTemplate" ref="jdbcTemplate" />
<property name="sessionFactory" ref="sessionFactory" />
<property name="periodService" ref="org.hisp.dhis.period.PeriodService" />
+ <property name="currentUserService" ref="org.hisp.dhis.user.CurrentUserService" />
+ <property name="organisationUnitService" ref="org.hisp.dhis.organisationunit.OrganisationUnitService" />
+ <property name="categoryService" ref="org.hisp.dhis.dataelement.DataElementCategoryService" />
+ <property name="dataApprovalLevelService" ref="org.hisp.dhis.dataapproval.DataApprovalLevelService" />
</bean>
<bean id="org.hisp.dhis.dataapproval.DataApprovalLevelStore" class="org.hisp.dhis.dataapproval.hibernate.HibernateDataApprovalLevelStore">
=== modified file 'dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java'
--- dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java 2014-10-23 21:47:57 +0000
+++ dhis-2/dhis-web/dhis-web-api/src/main/java/org/hisp/dhis/webapi/controller/DataApprovalController.java 2014-10-24 10:01:14 +0000
@@ -262,7 +262,7 @@
return;
}
- List<DataApprovalStatus> statusList = dataApprovalService.getUserDataApprovalsAndPermissions( dataSets, period );
+ List<DataApprovalStatus> statusList = dataApprovalService.getUserDataApprovalsAndPermissions( dataSets, asSet( period ) );
List<Map<String, Object>> list = new ArrayList<>();