← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 14753: WIP, function for constraining analytics queries on certain dimensions

 

------------------------------------------------------------
revno: 14753
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Tue 2014-04-08 20:25:25 +0200
message:
  WIP, function for constraining analytics queries on certain dimensions
modified:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/QueryPlanner.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/dimension/DefaultDimensionService.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/user/UserCredentials.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java	2014-04-02 05:00:27 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/user/UserCredentials.java	2014-04-08 18:25:25 +0000
@@ -36,11 +36,14 @@
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
 import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
 import org.hisp.dhis.common.BaseIdentifiableObject;
+import org.hisp.dhis.common.DimensionType;
+import org.hisp.dhis.common.DimensionalObject;
 import org.hisp.dhis.common.DxfNamespaces;
 import org.hisp.dhis.common.IdentifiableObjectUtils;
 import org.hisp.dhis.common.annotation.Scanned;
 import org.hisp.dhis.common.view.DetailedView;
 import org.hisp.dhis.common.view.ExportView;
+import org.hisp.dhis.dataelement.CategoryOptionGroupSet;
 import org.hisp.dhis.dataset.DataSet;
 
 import java.util.Collection;
@@ -91,6 +94,11 @@
      */
     @Scanned
     private Set<UserAuthorityGroup> userAuthorityGroups = new HashSet<UserAuthorityGroup>();
+    
+    /**
+     * Category option group set dimensions to constrain data analytics aggregation.
+     */
+    private Set<CategoryOptionGroupSet> cogsDimensionConstraints = new HashSet<CategoryOptionGroupSet>();
 
     /**
      * Date of the user's last login.
@@ -354,6 +362,32 @@
 
         return token.equals( this.restoreToken ) && code.equals( this.restoreCode );
     }
+    
+    /**
+     * Returns the dimensions to use as constrains (filters) in data analytics
+     * aggregation.
+     */
+    public Set<DimensionalObject> getDimensionConstraints()
+    {
+        Set<DimensionalObject> constraints = new HashSet<DimensionalObject>();
+        
+        for ( CategoryOptionGroupSet cogs : cogsDimensionConstraints )
+        {
+            cogs.setDimensionType( DimensionType.CATEGORYOPTION_GROUPSET );
+            constraints.add( cogs );
+        }
+        
+        return constraints;
+    }
+    
+    /**
+     * Indicates whether this user has dimension constraints.
+     */
+    public boolean hasDimensionConstraints()
+    {
+        Set<DimensionalObject> constraints = getDimensionConstraints();
+        return constraints != null && !constraints.isEmpty();
+    }
 
     // -------------------------------------------------------------------------
     // hashCode and equals
@@ -440,6 +474,21 @@
     }
 
     @JsonProperty
+    @JsonSerialize(contentAs = BaseIdentifiableObject.class)
+    @JsonView({ DetailedView.class, ExportView.class })
+    @JacksonXmlElementWrapper(localName = "cogsDimensionConstraints", namespace = DxfNamespaces.DXF_2_0)
+    @JacksonXmlProperty(localName = "cogsDimensionConstraint", namespace = DxfNamespaces.DXF_2_0)
+    public Set<CategoryOptionGroupSet> getCogsDimensionConstraints()
+    {
+        return cogsDimensionConstraints;
+    }
+
+    public void setCogsDimensionConstraints( Set<CategoryOptionGroupSet> cogsDimensionConstraints )
+    {
+        this.cogsDimensionConstraints = cogsDimensionConstraints;
+    }
+
+    @JsonProperty
     @JsonView({ DetailedView.class, ExportView.class })
     @JacksonXmlProperty(namespace = DxfNamespaces.DXF_2_0)
     public String getUsername()

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java	2014-04-01 12:44:59 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/DataQueryParams.java	2014-04-08 18:25:25 +0000
@@ -1136,7 +1136,7 @@
   
     /**
      * Retrieves the options for the the dimension or filter with the given 
-     * identifier. Returns null of the dimension of filter is not present.
+     * identifier. Returns null if the dimension or filter is not present.
      */
     public List<NameableObject> getDimensionOrFilter( String key )
     {
@@ -1196,6 +1196,16 @@
     {
         return dimensions.indexOf( new BaseDimensionalObject( key ) ) != -1 || filters.indexOf( new BaseDimensionalObject( key ) ) != -1;
     }
+
+    /**
+     * Indicates whether a dimension or filter which specifies dimension items 
+     * with the given identifier exists.
+     */
+    public boolean hasDimensionOrFilterWithItems( String key )
+    {
+        List<NameableObject> items = getDimensionOrFilter( key );
+        return items != null && !items.isEmpty();
+    }
     
     /**
      * Indicates whether a dimension with the given identifier exists. Returns

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/QueryPlanner.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/QueryPlanner.java	2014-03-18 08:10:10 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/QueryPlanner.java	2014-04-08 18:25:25 +0000
@@ -103,4 +103,14 @@
      * names respectively.
      */
     List<DataQueryParams> groupByPeriodType( DataQueryParams params );
+    
+    /**
+     * Applies dimension constraints to the given params. Dimension constraints
+     * with all accessible dimension items will be added as filters to this query.
+     * If current user has no dimension constraints, no action is taken. If the 
+     * constraint dimensions are already specified with accessible items in the 
+     * query, no action is taken. If the current user does not have accessible 
+     * items in any dimension constraint, an IllegalQueryException is thrown.
+     */
+    void applyDimensionConstraints( DataQueryParams params );
 }

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java	2014-04-08 17:05:58 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultAnalyticsService.java	2014-04-08 18:25:25 +0000
@@ -203,6 +203,8 @@
     @Override
     public Grid getAggregatedDataValues( DataQueryParams params )
     {
+        queryPlanner.applyDimensionConstraints( params );
+        
         queryPlanner.validate( params );
         
         params.conform();
@@ -758,6 +760,8 @@
         return params;
     }
     
+    // TODO verify that current user can read each dimension and dimension item
+    
     public List<DimensionalObject> getDimension( String dimension, List<String> items, Date relativePeriodDate, I18nFormat format )
     {        
         if ( DATA_X_DIM_ID.equals( dimension ) )

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java	2014-04-08 17:28:02 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/data/DefaultQueryPlanner.java	2014-04-08 18:25:25 +0000
@@ -47,6 +47,7 @@
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -57,6 +58,7 @@
 import org.hisp.dhis.analytics.QueryPlanner;
 import org.hisp.dhis.analytics.table.PartitionUtils;
 import org.hisp.dhis.common.BaseDimensionalObject;
+import org.hisp.dhis.common.DimensionService;
 import org.hisp.dhis.common.DimensionType;
 import org.hisp.dhis.common.DimensionalObject;
 import org.hisp.dhis.common.IllegalQueryException;
@@ -69,6 +71,8 @@
 import org.hisp.dhis.period.PeriodType;
 import org.hisp.dhis.system.util.MathUtils;
 import org.hisp.dhis.system.util.PaginatedList;
+import org.hisp.dhis.user.CurrentUserService;
+import org.hisp.dhis.user.User;
 import org.springframework.beans.factory.annotation.Autowired;
 
 /**
@@ -84,6 +88,12 @@
     @Autowired
     private OrganisationUnitService organisationUnitService;
     
+    @Autowired
+    private DimensionService dimensionService;
+    
+    @Autowired
+    private CurrentUserService currentUserService;
+    
     // -------------------------------------------------------------------------
     // DefaultQueryPlanner implementation
     // -------------------------------------------------------------------------
@@ -281,6 +291,57 @@
         return queryGroups;
     }
 
+    public void applyDimensionConstraints( DataQueryParams params )
+    {
+        User user = currentUserService.getCurrentUser();
+        
+        if ( params == null || user == null || user.getUserCredentials() == null )
+        {
+            return;
+        }
+        
+        if ( !user.getUserCredentials().hasDimensionConstraints()  )
+        {
+            return;
+        }
+        
+        Set<DimensionalObject> dimensionConstraints = user.getUserCredentials().getDimensionConstraints();
+        
+        for ( DimensionalObject dimension : dimensionConstraints )
+        {
+            // -----------------------------------------------------------------
+            // Check if constraint is already specified with items
+            // -----------------------------------------------------------------
+
+            if ( params.hasDimensionOrFilterWithItems( dimension.getUid() ) )
+            {
+                continue;
+            }
+
+            List<NameableObject> canReadItems = dimensionService.getCanReadDimensionItems( dimension.getDimension() );
+
+            // -----------------------------------------------------------------
+            // Check if current user has access to any items from constraint
+            // -----------------------------------------------------------------
+
+            if ( canReadItems == null || canReadItems.isEmpty() )
+            {
+                throw new IllegalQueryException( "Current user is constrained by a dimension but has access to no associated dimension items: " + dimension.getDimension() );
+            }
+
+            // -----------------------------------------------------------------
+            // Apply constraint as filter, and remove potential all-dimension
+            // -----------------------------------------------------------------
+
+            params.removeDimensionOrFilter( dimension.getDimension() );
+            
+            DimensionalObject constraint = new BaseDimensionalObject( dimension.getDimension(), 
+                dimension.getDimensionType(), null, dimension.getDisplayName(), canReadItems );
+            
+            params.getFilters().add( constraint );                        
+        }        
+    }
+    
     // -------------------------------------------------------------------------
     // Supportive methods
     // -------------------------------------------------------------------------

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/dimension/DefaultDimensionService.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/dimension/DefaultDimensionService.java	2014-03-27 06:07:15 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/dimension/DefaultDimensionService.java	2014-04-08 18:25:25 +0000
@@ -164,9 +164,11 @@
         List<NameableObject> items = new ArrayList<NameableObject>();
 
         if ( dimension != null && dimension.getItems() != null )
-        {
+        {            
             User user = currentUserService.getCurrentUser();
 
+            //TODO do this with query to improve performance
+
             for ( NameableObject item : dimension.getItems() )
             {
                 boolean canRead = aclService.canRead( user, item );
@@ -180,7 +182,7 @@
 
         return items;
     }
-
+        
     public DimensionType getDimensionType( String uid )
     {
         DataElementCategory cat = identifiableObjectManager.get( DataElementCategory.class, uid );