← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 11720: Analytics table generation, introduced abstraction / class AnalyticsTable which makes the impl mo...

 

------------------------------------------------------------
revno: 11720
committer: Lars Helge Øverland <larshelge@xxxxxxxxx>
branch nick: dhis2
timestamp: Wed 2013-08-21 00:48:23 +0200
message:
  Analytics table generation, introduced abstraction / class AnalyticsTable which makes the impl more flexible
added:
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTable.java
modified:
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTableManager.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/DefaultAnalyticsTableService.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTableManager.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTargetTableManager.java
  dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/PartitionUtils.java
  dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/PartitionUtilsTest.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
=== added file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTable.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTable.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTable.java	2013-08-20 22:48:23 +0000
@@ -0,0 +1,135 @@
+package org.hisp.dhis.analytics;
+
+/*
+ * Copyright (c) 2004-2012, University of Oslo
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright notice, this
+ *   list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of the HISP project nor the names of its contributors may
+ *   be used to endorse or promote products derived from this software without
+ *   specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+import org.hisp.dhis.period.Period;
+import org.hisp.dhis.program.Program;
+
+/**
+ * @author Lars Helge Overland
+ */
+public class AnalyticsTable
+{
+    private String baseName;
+    
+    private Period period;
+    
+    private Program program;
+    
+    public AnalyticsTable()
+    {
+    }
+
+    public AnalyticsTable( String baseName )
+    {
+        this.baseName = baseName;
+    }
+    
+    public AnalyticsTable( String baseName, Period period )
+    {
+        this.baseName = baseName;
+        this.period = period;
+    }
+    
+    public AnalyticsTable( String baseName, Period period, Program program )
+    {
+        this.baseName = baseName;
+        this.period = period;
+        this.program = program;
+    }
+    
+    public String getTableName()
+    {
+        String name = baseName;
+        
+        if ( period != null )
+        {
+            name += "_" + period.getIsoDate();
+        }
+        
+        if ( program != null )
+        {
+            name += "_" + program.getUid();
+        }
+        
+        return name;
+    }
+    
+    public String getTempTableName()
+    {
+        String name = baseName + AnalyticsTableManager.TABLE_TEMP_SUFFIX;
+
+        if ( period != null )
+        {
+            name += "_" + period.getIsoDate();
+        }
+        
+        if ( program != null )
+        {
+            name += "_" + program.getUid();
+        }
+        
+        return name;
+    }
+    
+    @Override
+    public String toString()
+    {
+        return getTableName();
+    }
+    
+    public String getBaseName()
+    {
+        return baseName;
+    }
+
+    public void setBaseName( String baseName )
+    {
+        this.baseName = baseName;
+    }
+
+    public Period getPeriod()
+    {
+        return period;
+    }
+
+    public void setPeriod( Period period )
+    {
+        this.period = period;
+    }
+
+    public Program getProgram()
+    {
+        return program;
+    }
+
+    public void setProgram( Program program )
+    {
+        this.program = program;
+    }
+}

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTableManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTableManager.java	2013-08-20 14:15:42 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/AnalyticsTableManager.java	2013-08-20 22:48:23 +0000
@@ -44,9 +44,21 @@
     public static final String COMPLETENESS_TARGET_TABLE_NAME = "completenesstarget";
     
     /**
-     * Returns table names.
-     */
-    List<String> getTables( boolean last3YearsOnly );
+     * Returns analytics tables which yearly partitions. Yearly partitions will
+     * be generated starting from the earliest existing data value until the
+     * latest existing data value.
+     * 
+     * @param last3YearsOnly whether to generate for last 3 years only.
+     */
+    List<AnalyticsTable> getTables( boolean last3YearsOnly );
+    
+    /**
+     * Returns analytics tables which yearly partitions.
+     * 
+     * @param earliest the start date for the first year to generate table partitions.
+     * @param latest the end date for the last year to generate table partitions.
+     */
+    List<AnalyticsTable> getTables( Date earliest, Date latest );
     
     /**
      * Checks if the database content is in valid state for analytics table generation.
@@ -68,13 +80,13 @@
      * 
      * @param tableName the table name.
      */
-    void createTable( String tableName );
+    void createTable( AnalyticsTable table );
     
     /**
      * Creates single indexes on the given columns of the analytics table with
      * the given name.
      * 
-     * @param indexes
+     * @param indexes the analytics indexes.
      */
     Future<?> createIndexesAsync( ConcurrentLinkedQueue<AnalyticsIndex> indexes );
     
@@ -82,17 +94,17 @@
      * Attempts to drop analytics table, then rename temporary table to analytics
      * table.
      * 
-     * @param tableName the name of the analytics table.
+     * @param table the analytics table.
      */
-    void swapTable( String tableName );
+    void swapTable( AnalyticsTable table );
     
     /**
      * Copies and denormalizes rows from data value table into analytics table.
      * The data range is based on the start date of the data value row.
      * 
-     * @param tables
+     * @param tables the analytics tables.
      */
-    Future<?> populateTableAsync( ConcurrentLinkedQueue<String> tables );    
+    Future<?> populateTableAsync( ConcurrentLinkedQueue<AnalyticsTable> tables );    
 
     /**
      * Returns a list of string arrays in where the first index holds the database
@@ -124,9 +136,9 @@
      * Checks whether the given table has no rows, if so drops the table. Returns
      * true if the table was empty and pruned, if not false.
      * 
-     * @param tableName the name of the table to prune.
+     * @param table the analytics table.
      */
-    boolean pruneTable( String tableName );
+    boolean pruneTable( AnalyticsTable table );
     
     /**
      * Drops the given table.
@@ -140,17 +152,17 @@
      * organisation unit level column values to null for the levels above the
      * given aggregation level.
      * 
-     * @param tables
+     * @param tables the analytics tables.
      * @param dataElements the data element uids to apply aggregation levels for.
      * @param aggregationLevel the aggregation level.
      */
-    Future<?> applyAggregationLevels( ConcurrentLinkedQueue<String> tables, Collection<String> dataElements, int aggregationLevel );
+    Future<?> applyAggregationLevels( ConcurrentLinkedQueue<AnalyticsTable> tables, Collection<String> dataElements, int aggregationLevel );
     
     /**
      * Performs vacuum or optimization of the given table. The type of operation
      * performed is dependent on the underlying DBMS.
      * 
-     * @param tables
+     * @param tables the analytics tables.
      */
-    Future<?> vacuumTablesAsync( ConcurrentLinkedQueue<String> tables );
+    Future<?> vacuumTablesAsync( ConcurrentLinkedQueue<AnalyticsTable> tables );
 }

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java	2013-08-20 14:15:42 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/AbstractJdbcTableManager.java	2013-08-20 22:48:23 +0000
@@ -37,6 +37,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.hisp.dhis.analytics.AnalyticsIndex;
+import org.hisp.dhis.analytics.AnalyticsTable;
 import org.hisp.dhis.analytics.AnalyticsTableManager;
 import org.hisp.dhis.common.CodeGenerator;
 import org.hisp.dhis.dataelement.DataElementCategoryService;
@@ -45,6 +46,7 @@
 import org.hisp.dhis.organisationunit.OrganisationUnitGroupService;
 import org.hisp.dhis.organisationunit.OrganisationUnitService;
 import org.hisp.dhis.period.Cal;
+import org.hisp.dhis.period.Period;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.jdbc.BadSqlGrammarException;
 import org.springframework.jdbc.core.JdbcTemplate;
@@ -84,13 +86,27 @@
     // Implementation
     // -------------------------------------------------------------------------
   
-    public List<String> getTables( boolean last3YearsOnly )
+    public List<AnalyticsTable> getTables( boolean last3YearsOnly )
     {
         Date threeYrsAgo = new Cal().subtract( Calendar.YEAR, 2 ).set( 1, 1 ).time();
         Date earliest = last3YearsOnly ? threeYrsAgo : getEarliestData();
         Date latest = getLatestData();
-        String tableName = getTableName();
-        List<String> tables = PartitionUtils.getTempTableNames( earliest, latest, tableName );
+        
+        return getTables( earliest, latest );
+    }
+
+    public List<AnalyticsTable> getTables( Date earliest, Date latest )
+    {
+        String baseName = getTableName();
+        
+        List<Period> periods = PartitionUtils.getPeriods( earliest, latest );
+
+        List<AnalyticsTable> tables = new ArrayList<AnalyticsTable>();
+        
+        for ( Period period : periods )
+        {
+            tables.add( new AnalyticsTable( baseName, period ) );
+        }
         
         return tables;
     }
@@ -124,15 +140,16 @@
         return null;
     }
 
-    public void swapTable( String tableName )
+    public void swapTable( AnalyticsTable table )
     {
-        final String realTable = tableName.replaceFirst( TABLE_TEMP_SUFFIX, "" );
+        final String tempTable = table.getTempTableName();
+        final String realTable = table.getTableName();
         
         final String sqlDrop = "drop table " + realTable;
         
         executeSilently( sqlDrop );
         
-        final String sqlAlter = "alter table " + tableName + " rename to " + realTable;
+        final String sqlAlter = "alter table " + tempTable + " rename to " + realTable;
         
         executeSilently( sqlAlter );
     }
@@ -151,8 +168,10 @@
         return columnNames;
     }
 
-    public boolean pruneTable( String tableName )
+    public boolean pruneTable( AnalyticsTable table )
     {
+        String tableName = table.getTempTableName();
+        
         if ( !hasRows( tableName ) )
         {
             final String sqlDrop = "drop table " + tableName;
@@ -168,18 +187,18 @@
     }
 
     @Async
-    public Future<?> vacuumTablesAsync( ConcurrentLinkedQueue<String> tables )
+    public Future<?> vacuumTablesAsync( ConcurrentLinkedQueue<AnalyticsTable> tables )
     {
         taskLoop : while ( true )
         {
-            String table = tables.poll();
+            AnalyticsTable table = tables.poll();
             
             if ( table == null )
             {
                 break taskLoop;
             }
             
-            final String sql = statementBuilder.getVacuum( table );
+            final String sql = statementBuilder.getVacuum( table.getTempTableName() );
             
             log.info( "Vacuum SQL: " + sql );
             

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/DefaultAnalyticsTableService.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/DefaultAnalyticsTableService.java	2013-08-20 14:15:42 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/DefaultAnalyticsTableService.java	2013-08-20 22:48:23 +0000
@@ -37,6 +37,7 @@
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 import org.hisp.dhis.analytics.AnalyticsIndex;
+import org.hisp.dhis.analytics.AnalyticsTable;
 import org.hisp.dhis.analytics.AnalyticsTableManager;
 import org.hisp.dhis.analytics.AnalyticsTableService;
 import org.hisp.dhis.common.IdentifiableObjectUtils;
@@ -90,7 +91,7 @@
             return;
         }
         
-        final List<String> tables = tableManager.getTables( last3YearsOnly );
+        final List<AnalyticsTable> tables = tableManager.getTables( last3YearsOnly );
         
         clock.logTime( "Partition tables: " + tables + ", last 3 years: " + last3YearsOnly );
         
@@ -134,15 +135,12 @@
 
     public void dropTables()
     {
-        List<String> tempTables = PartitionUtils.getTempTableNames( 
-            new Cal().set( 1900, 1, 1 ).time(), new Cal().set( 2100, 1, 1 ).time(), tableManager.getTableName() );
+        List<AnalyticsTable> tables = tableManager.getTables( new Cal().set( 1900, 1, 1 ).time(), new Cal().set( 2100, 1, 1 ).time() );
         
-        for ( String tempTable : tempTables )   
+        for ( AnalyticsTable table : tables )   
         {
-            String realTable = tempTable.replaceFirst( AnalyticsTableManager.TABLE_TEMP_SUFFIX, "" );
-            
-            tableManager.dropTable( tempTable );
-            tableManager.dropTable( realTable );            
+            tableManager.dropTable( table.getTableName() );
+            tableManager.dropTable( table.getTempTableName() );            
         }
     }
     
@@ -150,17 +148,17 @@
     // Supportive methods
     // -------------------------------------------------------------------------
   
-    private void createTables( List<String> tables )
+    private void createTables( List<AnalyticsTable> tables )
     {
-        for ( String table : tables )
+        for ( AnalyticsTable table : tables )
         {
             tableManager.createTable( table );
         }
     }
     
-    private void populateTables( List<String> tables )
+    private void populateTables( List<AnalyticsTable> tables )
     {
-        ConcurrentLinkedQueue<String> tableQ = new ConcurrentLinkedQueue<String>( tables );
+        ConcurrentLinkedQueue<AnalyticsTable> tableQ = new ConcurrentLinkedQueue<AnalyticsTable>( tables );
         
         List<Future<?>> futures = new ArrayList<Future<?>>();
         
@@ -172,9 +170,9 @@
         ConcurrentUtils.waitForCompletion( futures );
     }
     
-    private void pruneTables( List<String> tables )
+    private void pruneTables( List<AnalyticsTable> tables )
     {
-        Iterator<String> iterator = tables.iterator();
+        Iterator<AnalyticsTable> iterator = tables.iterator();
         
         while ( iterator.hasNext() )
         {
@@ -185,7 +183,7 @@
         }
     }
     
-    private void applyAggregationLevels( List<String> tables )
+    private void applyAggregationLevels( List<AnalyticsTable> tables )
     {
         int maxLevels = organisationUnitService.getMaxOfOrganisationUnitLevels();
         
@@ -201,7 +199,7 @@
                 continue levelLoop;
             }
                         
-            ConcurrentLinkedQueue<String> tableQ = new ConcurrentLinkedQueue<String>( tables );
+            ConcurrentLinkedQueue<AnalyticsTable> tableQ = new ConcurrentLinkedQueue<AnalyticsTable>( tables );
 
             List<Future<?>> futures = new ArrayList<Future<?>>();
             
@@ -214,17 +212,17 @@
         }
     }
     
-    private void createIndexes( List<String> tables )
+    private void createIndexes( List<AnalyticsTable> tables )
     {
         ConcurrentLinkedQueue<AnalyticsIndex> indexes = new ConcurrentLinkedQueue<AnalyticsIndex>();
         
         List<String> columns = tableManager.getDimensionColumnNames();
         
-        for ( String table : tables )
+        for ( AnalyticsTable table : tables )
         {
             for ( String column : columns )
             {
-                indexes.add( new AnalyticsIndex( table, column ) );
+                indexes.add( new AnalyticsIndex( table.getTempTableName(), column ) );
             }
         }
         
@@ -240,9 +238,9 @@
         ConcurrentUtils.waitForCompletion( futures );
     }
 
-    private void vacuumTables( List<String> tables )
+    private void vacuumTables( List<AnalyticsTable> tables )
     {
-        ConcurrentLinkedQueue<String> tableQ = new ConcurrentLinkedQueue<String>( tables );
+        ConcurrentLinkedQueue<AnalyticsTable> tableQ = new ConcurrentLinkedQueue<AnalyticsTable>( tables );
         
         List<Future<?>> futures = new ArrayList<Future<?>>();
         
@@ -254,9 +252,9 @@
         ConcurrentUtils.waitForCompletion( futures );        
     }
     
-    private void swapTables( List<String> tables )
+    private void swapTables( List<AnalyticsTable> tables )
     {
-        for ( String table : tables )
+        for ( AnalyticsTable table : tables )
         {
             tableManager.swapTable( table );
         }

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java	2013-06-25 10:58:15 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcAnalyticsTableManager.java	2013-08-20 22:48:23 +0000
@@ -37,12 +37,12 @@
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Future;
 
+import org.hisp.dhis.analytics.AnalyticsTable;
 import org.hisp.dhis.analytics.DataQueryParams;
 import org.hisp.dhis.dataelement.DataElementCategory;
 import org.hisp.dhis.dataelement.DataElementGroupSet;
 import org.hisp.dhis.organisationunit.OrganisationUnitGroupSet;
 import org.hisp.dhis.organisationunit.OrganisationUnitLevel;
-import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
 import org.hisp.dhis.system.util.DateUtils;
 import org.hisp.dhis.system.util.MathUtils;
@@ -81,8 +81,10 @@
         return "analytics";
     }
         
-    public void createTable( String tableName )
+    public void createTable( AnalyticsTable table )
     {
+        final String tableName = table.getTempTableName();
+        
         final String dbl = statementBuilder.getDoubleColumnType();
         
         final String sqlDrop = "drop table " + tableName;
@@ -104,44 +106,39 @@
     }
     
     @Async
-    public Future<?> populateTableAsync( ConcurrentLinkedQueue<String> tables )
+    public Future<?> populateTableAsync( ConcurrentLinkedQueue<AnalyticsTable> tables )
     {
         final String dbl = statementBuilder.getDoubleColumnType();
         
         taskLoop : while ( true )
         {
-            String table = tables.poll();
+            AnalyticsTable table = tables.poll();
                 
             if ( table == null )
             {
                 break taskLoop;
             }
             
-            Period period = PartitionUtils.getPeriod( table );
-            
-            Date startDate = period.getStartDate();
-            Date endDate = period.getEndDate();
-            
             String intClause = 
                 "dv.value " + statementBuilder.getRegexpMatch() + " '" + MathUtils.NUMERIC_LENIENT_REGEXP + "' " +
                 "and ( dv.value != '0' or de.aggregationtype = 'average' or de.zeroissignificant = true ) ";
             
-            populateTable( table, startDate, endDate, "cast(dv.value as " + dbl + ")", "int", intClause );
+            populateTable( table, "cast(dv.value as " + dbl + ")", "int", intClause );
             
-            populateTable( table, startDate, endDate, "1" , "bool", "dv.value = 'true'" );
+            populateTable( table, "1" , "bool", "dv.value = 'true'" );
     
-            populateTable( table, startDate, endDate, "0" , "bool", "dv.value = 'false'" );
+            populateTable( table, "0" , "bool", "dv.value = 'false'" );
         }
     
         return null;
     }
     
-    private void populateTable( String tableName, Date startDate, Date endDate, String valueExpression, String valueType, String clause )
+    private void populateTable( AnalyticsTable table, String valueExpression, String valueType, String clause )
     {
-        final String start = DateUtils.getMediumDateString( startDate );
-        final String end = DateUtils.getMediumDateString( endDate );
+        final String start = DateUtils.getMediumDateString( table.getPeriod().getStartDate() );
+        final String end = DateUtils.getMediumDateString( table.getPeriod().getEndDate() );
         
-        String sql = "insert into " + tableName + " (";
+        String sql = "insert into " + table.getTempTableName() + " (";
         
         for ( String[] col : getDimensionColumns() )
         {
@@ -256,18 +253,18 @@
     }
     
     @Async
-    public Future<?> applyAggregationLevels( ConcurrentLinkedQueue<String> tables, Collection<String> dataElements, int aggregationLevel )
+    public Future<?> applyAggregationLevels( ConcurrentLinkedQueue<AnalyticsTable> tables, Collection<String> dataElements, int aggregationLevel )
     {
         taskLoop : while ( true )
         {
-            String table = tables.poll();
+            AnalyticsTable table = tables.poll();
                 
             if ( table == null )
             {
                 break taskLoop;
             }
             
-            StringBuilder sql = new StringBuilder( "update " + table + " set " );
+            StringBuilder sql = new StringBuilder( "update " + table.getTempTableName() + " set " );
             
             for ( int i = 0; i < aggregationLevel; i++ )
             {

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTableManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTableManager.java	2013-05-07 08:37:33 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTableManager.java	2013-08-20 22:48:23 +0000
@@ -34,9 +34,9 @@
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Future;
 
+import org.hisp.dhis.analytics.AnalyticsTable;
 import org.hisp.dhis.organisationunit.OrganisationUnitGroupSet;
 import org.hisp.dhis.organisationunit.OrganisationUnitLevel;
-import org.hisp.dhis.period.Period;
 import org.hisp.dhis.period.PeriodType;
 import org.hisp.dhis.system.util.DateUtils;
 import org.springframework.scheduling.annotation.Async;
@@ -57,8 +57,10 @@
         return "completeness";
     }
     
-    public void createTable( String tableName )
+    public void createTable( AnalyticsTable table )
     {
+        final String tableName = table.getTempTableName();
+        
         final String sqlDrop = "drop table " + tableName;
         
         executeSilently( sqlDrop );
@@ -78,23 +80,21 @@
     }
     
     @Async
-    public Future<?> populateTableAsync( ConcurrentLinkedQueue<String> tables )
+    public Future<?> populateTableAsync( ConcurrentLinkedQueue<AnalyticsTable> tables )
     {
         taskLoop : while ( true )
         {
-            String table = tables.poll();
+            AnalyticsTable table = tables.poll();
                 
             if ( table == null )
             {
                 break taskLoop;
             }
             
-            Period period = PartitionUtils.getPeriod( table );
-            
-            final String start = DateUtils.getMediumDateString( period.getStartDate() );
-            final String end = DateUtils.getMediumDateString( period.getEndDate() );
+            final String start = DateUtils.getMediumDateString( table.getPeriod().getStartDate() );
+            final String end = DateUtils.getMediumDateString( table.getPeriod().getEndDate() );
         
-            String insert = "insert into " + table + " (";
+            String insert = "insert into " + table.getTempTableName() + " (";
             
             for ( String[] col : getDimensionColumns() )
             {
@@ -188,7 +188,7 @@
     }
 
     @Async
-    public Future<?> applyAggregationLevels( ConcurrentLinkedQueue<String> tables, Collection<String> dataElements, int aggregationLevel )
+    public Future<?> applyAggregationLevels( ConcurrentLinkedQueue<AnalyticsTable> tables, Collection<String> dataElements, int aggregationLevel )
     {
         return null; // Not relevant
     }

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTargetTableManager.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTargetTableManager.java	2013-05-07 08:37:33 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/JdbcCompletenessTargetTableManager.java	2013-08-20 22:48:23 +0000
@@ -34,6 +34,7 @@
 import java.util.concurrent.ConcurrentLinkedQueue;
 import java.util.concurrent.Future;
 
+import org.hisp.dhis.analytics.AnalyticsTable;
 import org.hisp.dhis.organisationunit.OrganisationUnitGroupSet;
 import org.hisp.dhis.organisationunit.OrganisationUnitLevel;
 import org.springframework.scheduling.annotation.Async;
@@ -44,6 +45,14 @@
 public class JdbcCompletenessTargetTableManager
     extends AbstractJdbcTableManager
 {
+    @Override
+    public List<AnalyticsTable> getTables( boolean last3YearsOnly )
+    {
+        List<AnalyticsTable> tables = new ArrayList<AnalyticsTable>();
+        tables.add( new AnalyticsTable( getTableName() ) );
+        return tables;
+    }
+    
     public boolean validState()
     {
         return true;
@@ -54,8 +63,10 @@
         return "completenesstarget";
     }
 
-    public void createTable( String tableName )
+    public void createTable( AnalyticsTable table )
     {
+        final String tableName = table.getTempTableName();
+        
         final String sqlDrop = "drop table " + tableName;
         
         executeSilently( sqlDrop );
@@ -75,18 +86,18 @@
     }
 
     @Async
-    public Future<?> populateTableAsync( ConcurrentLinkedQueue<String> tables )
+    public Future<?> populateTableAsync( ConcurrentLinkedQueue<AnalyticsTable> tables )
     {
         taskLoop : while ( true )
         {
-            String table = tables.poll();
+            AnalyticsTable table = tables.poll();
                 
             if ( table == null )
             {
                 break taskLoop;
             }
             
-            String sql = "insert into " + table + " (";
+            String sql = "insert into " + table.getTempTableName() + " (";
     
             for ( String[] col : getDimensionColumns() )
             {
@@ -156,7 +167,7 @@
     }
 
     @Async
-    public Future<?> applyAggregationLevels( ConcurrentLinkedQueue<String> tables, Collection<String> dataElements, int aggregationLevel )
+    public Future<?> applyAggregationLevels( ConcurrentLinkedQueue<AnalyticsTable> tables, Collection<String> dataElements, int aggregationLevel )
     {
         return null; // Not relevant
     }

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/PartitionUtils.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/PartitionUtils.java	2013-05-23 13:38:20 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/table/PartitionUtils.java	2013-08-20 22:48:23 +0000
@@ -28,11 +28,9 @@
  */
 
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
 
-import org.hisp.dhis.analytics.AnalyticsTableManager;
 import org.hisp.dhis.common.ListMap;
 import org.hisp.dhis.common.NameableObject;
 import org.hisp.dhis.period.Period;
@@ -48,32 +46,19 @@
     
     private static final String SEP = "_";
 
-    public static List<String> getTempTableNames( Date earliest, Date latest, String tableName )
-    {   
-        if ( earliest == null || latest == null )
-        {
-            return new ArrayList<String>( Arrays.asList( tableName + AnalyticsTableManager.TABLE_TEMP_SUFFIX ) );
-        }
-        
-        if ( earliest.after( latest ) )
-        {
-            throw new IllegalArgumentException( "Earliest date is after latest: " + earliest + ", " + latest );
-        }
-        
-        List<String> tables = new ArrayList<String>();
+    public static List<Period> getPeriods( Date earliest, Date latest )
+    {
+        List<Period> periods = new ArrayList<Period>();
         
         Period period = PERIODTYPE.createPeriod( earliest );
         
         while ( period != null && period.getStartDate().before( latest ) )
         {
-            String table = tableName + AnalyticsTableManager.TABLE_TEMP_SUFFIX + SEP + period.getIsoDate();
-            
-            tables.add( table );
-            
+            periods.add( period );            
             period = PERIODTYPE.getNextPeriod( period );
         }
         
-        return tables;
+        return periods;
     }
     
     public static String getTableName( Period period, String tableName )

=== modified file 'dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/PartitionUtilsTest.java'
--- dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/PartitionUtilsTest.java	2013-05-23 13:38:20 +0000
+++ dhis-2/dhis-services/dhis-service-analytics/src/test/java/org/hisp/dhis/analytics/table/PartitionUtilsTest.java	2013-08-20 22:48:23 +0000
@@ -34,9 +34,6 @@
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
-import java.util.Date;
-import java.util.List;
-
 import org.hisp.dhis.common.ListMap;
 import org.hisp.dhis.common.NameableObject;
 import org.hisp.dhis.period.Cal;
@@ -51,23 +48,7 @@
 {
     private static final String TABLE_NAME_TEMP = ANALYTICS_TABLE_NAME + TABLE_TEMP_SUFFIX;
     private static final String TABLE_NAME = ANALYTICS_TABLE_NAME;
-    
-    @Test
-    public void testGetTableNames()
-    {
-        Cal cal = new Cal();
-        Date earliest = cal.set( 2000, 5, 4 ).time();
-        Date latest = cal.set( 2003, 2, 10 ).time();
-        
-        List<String> tables = PartitionUtils.getTempTableNames( earliest, latest, TABLE_NAME );
-        
-        assertEquals( 4, tables.size() );
-        assertTrue( tables.contains( TABLE_NAME_TEMP + "_2000" ) );
-        assertTrue( tables.contains( TABLE_NAME_TEMP + "_2001" ) );
-        assertTrue( tables.contains( TABLE_NAME_TEMP + "_2002" ) );
-        assertTrue( tables.contains( TABLE_NAME_TEMP + "_2003" ) );
-    }
-    
+        
     @Test
     public void testGetTable()
     {