← Back to team overview

dhis2-devs team mailing list archive

[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<>();