← Back to team overview

dhis2-devs team mailing list archive

[Branch ~dhis2-devs-core/dhis2/trunk] Rev 8949: Improve program-validation-rule (WIP).

 

------------------------------------------------------------
revno: 8949
committer: Tran Chau <tran.hispvietnam@xxxxxxxxx>
branch nick: dhis2
timestamp: Thu 2012-11-08 20:33:53 +0700
message:
  Improve program-validation-rule (WIP).
added:
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramExpression.java
  dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramExpressionService.java
  dhis-2/dhis-services/dhis-service-mapping/
  dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/program/DefaultProgramExpressionService.java
  dhis-2/dhis-services/dhis-service-patient/src/main/resources/org/hisp/dhis/program/hibernate/ProgramExpression.hbm.xml
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/java/org/hisp/dhis/patient/action/validation/GetProgramExpressionDescriptionAction.java
modified:
  dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseentry/ValidateProgramInstanceAction.java
  dhis-2/dhis-web/dhis-web-light/src/main/java/org/hisp/dhis/light/namebaseddataentry/action/SaveProgramStageFormAction.java
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/addSingleProgramValidationForm.vm
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/javascript/programValidation.js
  dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/programValidationList.vm


--
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-api/src/main/java/org/hisp/dhis/program/ProgramExpression.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramExpression.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramExpression.java	2012-11-08 13:33:53 +0000
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2004-2009, 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.
+ */
+
+package org.hisp.dhis.program;
+
+import java.io.Serializable;
+
+/**
+ * @author Chau Thu Tran
+ * 
+ * @version ProgramExpression.java 2:48:32 PM Nov 8, 2012 $
+ */
+public class ProgramExpression
+    implements Serializable
+{
+    private static final long serialVersionUID = -2807997671779497354L;
+    
+
+    public static final String SEPARATOR_ID = "\\.";
+
+    public static String OBJECT_PROGRAM_STAGE_DATAELEMENT = "DE";
+
+    public static final String SEPARATOR_OBJECT = ":";
+    
+
+    public static final int BEFORE_CURRENT_DATE = 1;
+
+    public static final int BEFORE_OR_EQUALS_TO_CURRENT_DATE = 2;
+
+    public static final int AFTER_CURRENT_DATE = 3;
+
+    public static final int AFTER_OR_EQUALS_TO_CURRENT_DATE = 4;
+    
+
+    public static final int BEFORE_DUE_DATE = -1;
+
+    public static final int BEFORE_OR_EQUALS_TO_DUE_DATE = -2;
+
+    public static final int AFTER_DUE_DATE = -3;
+
+    public static final int AFTER_OR_EQUALS_TO_DUE_DATE = -4;
+    
+    public static final int BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS = -5;
+    
+    public static final String NOT_NULL_VALUE_IN_EXPRESSION = "NOT-NULL-VALUE";
+
+    
+    private int id;
+
+    private String expression;
+
+    private String description;
+
+    // -------------------------------------------------------------------------
+    // Constructors
+    // -------------------------------------------------------------------------
+
+    public ProgramExpression()
+    {
+
+    }
+
+    public ProgramExpression( String expression, String description )
+    {
+        this.expression = expression;
+        this.description = description;
+    }
+
+    // -------------------------------------------------------------------------
+    // Equals and hashCode
+    // -------------------------------------------------------------------------
+
+    @Override
+    public int hashCode()
+    {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((description == null) ? 0 : description.hashCode());
+        result = prime * result + ((expression == null) ? 0 : expression.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals( Object obj )
+    {
+        if ( this == obj )
+            return true;
+        if ( obj == null )
+            return false;
+        if ( getClass() != obj.getClass() )
+            return false;
+        ProgramExpression other = (ProgramExpression) obj;
+        if ( description == null )
+        {
+            if ( other.description != null )
+                return false;
+        }
+        else if ( !description.equals( other.description ) )
+            return false;
+        if ( expression == null )
+        {
+            if ( other.expression != null )
+                return false;
+        }
+        else if ( !expression.equals( other.expression ) )
+            return false;
+        return true;
+    }
+
+    // -------------------------------------------------------------------------
+    // Getters and setters
+    // -------------------------------------------------------------------------
+
+    public int getId()
+    {
+        return id;
+    }
+
+    public void setId( int id )
+    {
+        this.id = id;
+    }
+
+    public String getExpression()
+    {
+        return expression;
+    }
+
+    public void setExpression( String expression )
+    {
+        this.expression = expression;
+    }
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+    public void setDescription( String description )
+    {
+        this.description = description;
+    }
+
+}

=== added file 'dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramExpressionService.java'
--- dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramExpressionService.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-api/src/main/java/org/hisp/dhis/program/ProgramExpressionService.java	2012-11-08 13:33:53 +0000
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2004-2009, 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.
+ */
+
+package org.hisp.dhis.program;
+
+import java.util.Collection;
+
+
+/**
+ * @author Chau Thu Tran
+ *
+ * @version ProgramExpressionService.java 2:59:58 PM Nov 8, 2012 $
+ */
+public interface ProgramExpressionService
+{
+    String ID = ProgramExpressionService.class.getName();
+    
+    int addProgramExpression(ProgramExpression programExpression );
+    
+    void updateProgramExpression(ProgramExpression programExpression );
+    
+    void deleteProgramExpression ( ProgramExpression programExpression );
+    
+    ProgramExpression getProgramExpression( int id );
+    
+    Collection<ProgramExpression> getAllProgramExpressions();
+    
+    String getProgramExpressionValue( ProgramExpression programExpression, ProgramStageInstance programStageInstance );
+    
+    String getExpressionDescription( String programExpression );
+    
+}

=== added directory 'dhis-2/dhis-services/dhis-service-mapping'
=== added file 'dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/program/DefaultProgramExpressionService.java'
--- dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/program/DefaultProgramExpressionService.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-patient/src/main/java/org/hisp/dhis/program/DefaultProgramExpressionService.java	2012-11-08 13:33:53 +0000
@@ -0,0 +1,204 @@
+/*
+ * Copyright (c) 2004-2009, 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.
+ */
+
+package org.hisp.dhis.program;
+
+import static org.hisp.dhis.program.ProgramExpression.OBJECT_PROGRAM_STAGE_DATAELEMENT;
+import static org.hisp.dhis.program.ProgramExpression.SEPARATOR_ID;
+import static org.hisp.dhis.program.ProgramExpression.SEPARATOR_OBJECT;
+
+import java.util.Collection;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.hisp.dhis.common.GenericStore;
+import org.hisp.dhis.dataelement.DataElement;
+import org.hisp.dhis.dataelement.DataElementService;
+import org.hisp.dhis.patientdatavalue.PatientDataValue;
+import org.hisp.dhis.patientdatavalue.PatientDataValueService;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * @author Chau Thu Tran
+ * 
+ * @version DefaultProgramExpressionService.java 3:06:24 PM Nov 8, 2012 $
+ */
+@Transactional
+public class DefaultProgramExpressionService
+    implements ProgramExpressionService
+{
+    private final String regExp = "\\[" + OBJECT_PROGRAM_STAGE_DATAELEMENT + SEPARATOR_OBJECT + "([a-zA-Z0-9\\- ]+["
+        + SEPARATOR_ID + "[0-9]*]*)" + "\\]";
+    
+    private final String INVALID_CONDITION = "Invalid condition";
+
+    // -------------------------------------------------------------------------
+    // Dependencies
+    // -------------------------------------------------------------------------
+
+    private GenericStore<ProgramExpression> programExpressionStore;
+
+    public void setProgramExpressionStore( GenericStore<ProgramExpression> programExpressionStore )
+    {
+        this.programExpressionStore = programExpressionStore;
+    }
+
+    private ProgramStageService programStageService;
+
+    public void setProgramStageService( ProgramStageService programStageService )
+    {
+        this.programStageService = programStageService;
+    }
+
+    private PatientDataValueService patientDataValueService;
+
+    public void setPatientDataValueService( PatientDataValueService patientDataValueService )
+    {
+        this.patientDataValueService = patientDataValueService;
+    }
+
+    private DataElementService dataElementService;
+
+    public void setDataElementService( DataElementService dataElementService )
+    {
+        this.dataElementService = dataElementService;
+    }
+
+    // -------------------------------------------------------------------------
+    // ProgramExpression CRUD operations
+    // -------------------------------------------------------------------------
+
+    @Override
+    public int addProgramExpression( ProgramExpression programExpression )
+    {
+        return programExpressionStore.save( programExpression );
+    }
+
+    @Override
+    public void updateProgramExpression( ProgramExpression programExpression )
+    {
+        programExpressionStore.update( programExpression );
+    }
+
+    @Override
+    public void deleteProgramExpression( ProgramExpression programExpression )
+    {
+        programExpressionStore.delete( programExpression );
+    }
+
+    @Override
+    public ProgramExpression getProgramExpression( int id )
+    {
+        return programExpressionStore.get( id );
+    }
+
+    @Override
+    public Collection<ProgramExpression> getAllProgramExpressions()
+    {
+        return programExpressionStore.getAll();
+    }
+
+    @Override
+    public String getProgramExpressionValue( ProgramExpression programExpression,
+        ProgramStageInstance programStageInstance )
+    {
+        StringBuffer description = new StringBuffer();
+
+        Pattern pattern = Pattern.compile( regExp );
+        Matcher matcher = pattern.matcher( programExpression.getExpression() );
+        while ( matcher.find() )
+        {
+            String match = matcher.group();
+
+            PatientDataValue dataValue = getPatientDataValue( match, programStageInstance );
+
+            if ( dataValue == null )
+            {
+                return null;
+            }
+
+            matcher.appendReplacement( description, dataValue.getValue() );
+        }
+
+        matcher.appendTail( description );
+
+        return description.toString();
+    }
+
+    @Override
+    public String getExpressionDescription( String programExpression )
+    {
+        StringBuffer description = new StringBuffer();
+
+        Pattern pattern = Pattern.compile( regExp );
+        Matcher matcher = pattern.matcher( programExpression );
+        while ( matcher.find() )
+        {
+            String match = matcher.group();
+            match = match.replaceAll( "[\\[\\]]", "" );
+
+            String[] info = match.split( SEPARATOR_OBJECT );
+            String[] ids = info[1].split( SEPARATOR_ID );
+
+            String programStageId = ids[0];
+            ProgramStage programStage = programStageService.getProgramStage( Integer.parseInt( programStageId ) );
+
+            int dataElementId = Integer.parseInt( ids[1] );
+            DataElement dataElement = dataElementService.getDataElement( dataElementId );
+
+            if ( programStage == null || dataElement == null )
+            {
+                return INVALID_CONDITION;
+            }
+
+            matcher.appendReplacement( description, programStage.getName() + SEPARATOR_ID + dataElement.getName() );
+        }
+
+        matcher.appendTail( description );
+
+        return description.toString();
+    }
+
+    // -------------------------------------------------------------------------
+    // Supportive methods
+    // -------------------------------------------------------------------------
+
+    private PatientDataValue getPatientDataValue( String expression, ProgramStageInstance programStageInstance )
+    {
+        expression = expression.replaceAll( "[\\[\\]]", "" );
+
+        String[] info = expression.split( SEPARATOR_OBJECT );
+        String[] ids = info[1].split( SEPARATOR_ID );
+
+        int dataElementId = Integer.parseInt( ids[1] );
+        DataElement dataElement = dataElementService.getDataElement( dataElementId );
+
+        PatientDataValue dataValue = patientDataValueService.getPatientDataValue( programStageInstance, dataElement );
+
+        return dataValue;
+    }
+}

=== added file 'dhis-2/dhis-services/dhis-service-patient/src/main/resources/org/hisp/dhis/program/hibernate/ProgramExpression.hbm.xml'
--- dhis-2/dhis-services/dhis-service-patient/src/main/resources/org/hisp/dhis/program/hibernate/ProgramExpression.hbm.xml	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-services/dhis-service-patient/src/main/resources/org/hisp/dhis/program/hibernate/ProgramExpression.hbm.xml	2012-11-08 13:33:53 +0000
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!DOCTYPE hibernate-mapping PUBLIC
+  "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
+  "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd";>
+
+<hibernate-mapping>
+  <class name="org.hisp.dhis.program.ProgramExpression" table="programexpression">
+
+    <cache usage="read-write" />
+
+    <id name="id" column="programexpressionid">
+      <generator class="native" />
+    </id>
+
+    <property name="description" />
+
+    <property name="expression" type="text" />
+
+  </class>
+</hibernate-mapping>

=== modified file 'dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseentry/ValidateProgramInstanceAction.java'
--- dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseentry/ValidateProgramInstanceAction.java	2012-11-06 08:39:16 +0000
+++ dhis-2/dhis-web/dhis-web-caseentry/src/main/java/org/hisp/dhis/caseentry/action/caseentry/ValidateProgramInstanceAction.java	2012-11-08 13:33:53 +0000
@@ -27,16 +27,6 @@
 
 package org.hisp.dhis.caseentry.action.caseentry;
 
-import static org.hisp.dhis.program.ProgramValidation.AFTER_CURRENT_DATE;
-import static org.hisp.dhis.program.ProgramValidation.AFTER_DUE_DATE;
-import static org.hisp.dhis.program.ProgramValidation.AFTER_OR_EQUALS_TO_CURRENT_DATE;
-import static org.hisp.dhis.program.ProgramValidation.AFTER_OR_EQUALS_TO_DUE_DATE;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_CURRENT_DATE;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_DUE_DATE;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_OR_EQUALS_TO_CURRENT_DATE;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_OR_EQUALS_TO_DUE_DATE;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -45,7 +35,6 @@
 
 import org.hisp.dhis.caseentry.state.SelectedStateManager;
 import org.hisp.dhis.dataelement.DataElement;
-import org.hisp.dhis.i18n.I18n;
 import org.hisp.dhis.i18n.I18nFormat;
 import org.hisp.dhis.program.ProgramStageInstance;
 import org.hisp.dhis.program.ProgramValidation;
@@ -73,8 +62,6 @@
     // Input
     // -------------------------------------------------------------------------
 
-    private I18n i18n;
-
     private I18nFormat format;
 
     // -------------------------------------------------------------------------
@@ -128,11 +115,6 @@
         return resultDEMultiStages;
     }
 
-    public void setI18n( I18n i18n )
-    {
-        this.i18n = i18n;
-    }
-
     // -------------------------------------------------------------------------
     // Action implementation
     // -------------------------------------------------------------------------
@@ -172,7 +154,7 @@
         {
             for ( ProgramValidation validation : validations )
             {
-                ProgramValidationResult validationResult = programValidationService.runValidation( validation, programStageInstance, format );
+                ProgramValidationResult validationResult = programValidationService.validate( validation, programStageInstance, format );
 
                 if ( validationResult != null )
                 {
@@ -180,86 +162,5 @@
                 }
             }
         }
-
-        if ( !programValidationResults.isEmpty() )
-        {
-            leftsideFormulaMap = new HashMap<Integer, String>( programValidationResults.size() );
-            rightsideFormulaMap = new HashMap<Integer, String>( programValidationResults.size() );
-
-            for ( ProgramValidationResult validationResult : programValidationResults )
-            {
-                leftsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                    programValidationService.getValidationDescription( validationResult.getProgramValidation().getLeftSide() ) );
-
-                if ( validationResult.getProgramValidation().getDateType() )
-                {
-                    String rightSide = validationResult.getProgramValidation().getRightSide();
-                    int index = rightSide.indexOf( 'D' );
-                    if ( index < 0 )
-                    {
-                        int rightValidation = Integer.parseInt( rightSide );
-
-                        switch ( rightValidation )
-                        {
-                        case BEFORE_CURRENT_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), i18n.getString( "before_current_date" ) );
-                            break;
-                        case BEFORE_OR_EQUALS_TO_CURRENT_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "before_or_equals_to_current_date" ) );
-                            break;
-                        case AFTER_CURRENT_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), i18n.getString( "after_current_date" ) );
-                            break;
-                        case AFTER_OR_EQUALS_TO_CURRENT_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "after_or_equals_to_current_date" ) );
-                            break;
-                        case BEFORE_DUE_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), i18n.getString( "before_due_date" ) );
-                            break;
-                        case BEFORE_OR_EQUALS_TO_DUE_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "before_or_equals_to_due_date" ) );
-                            break;
-                        case AFTER_DUE_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), i18n.getString( "after_due_date" ) );
-                            break;
-                        case AFTER_OR_EQUALS_TO_DUE_DATE:
-                            rightsideFormulaMap
-                                .put( validationResult.getProgramValidation().getId(), i18n.getString( "after_or_equals_to_due_date" ) );
-                            break;
-                        default:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), "" );
-                            break;
-
-                        }
-                    }
-                    else
-                    {
-                        int rightValidation = Integer.parseInt( rightSide.substring( 0, index ) );
-
-                        int daysValue = Integer.parseInt( rightSide.substring( index + 1, rightSide.length() ) );
-
-                        if ( rightValidation == BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS )
-                        {
-                            rightsideFormulaMap.put(
-                                validationResult.getProgramValidation().getId(),
-                                i18n.getString( "in_range_due_date_plus_or_minus" ) + " " + daysValue
-                                    + i18n.getString( "days" ) );
-                        }
-                    }
-                }
-                else if ( validationResult.getProgramValidation().getRightSide().equals( "1==1" ) )
-                {
-                    rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), "" );
-                }
-                else
-                {
-                    rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                        programValidationService.getValidationDescription( validationResult.getProgramValidation().getRightSide() ) );
-                }
-            }
-        }
     }
 }

=== modified file 'dhis-2/dhis-web/dhis-web-light/src/main/java/org/hisp/dhis/light/namebaseddataentry/action/SaveProgramStageFormAction.java'
--- dhis-2/dhis-web/dhis-web-light/src/main/java/org/hisp/dhis/light/namebaseddataentry/action/SaveProgramStageFormAction.java	2012-11-07 12:55:09 +0000
+++ dhis-2/dhis-web/dhis-web-light/src/main/java/org/hisp/dhis/light/namebaseddataentry/action/SaveProgramStageFormAction.java	2012-11-08 13:33:53 +0000
@@ -27,16 +27,6 @@
 
 package org.hisp.dhis.light.namebaseddataentry.action;
 
-import static org.hisp.dhis.program.ProgramValidation.AFTER_CURRENT_DATE;
-import static org.hisp.dhis.program.ProgramValidation.AFTER_DUE_DATE;
-import static org.hisp.dhis.program.ProgramValidation.AFTER_OR_EQUALS_TO_CURRENT_DATE;
-import static org.hisp.dhis.program.ProgramValidation.AFTER_OR_EQUALS_TO_DUE_DATE;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_CURRENT_DATE;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_DUE_DATE;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_OR_EQUALS_TO_CURRENT_DATE;
-import static org.hisp.dhis.program.ProgramValidation.BEFORE_OR_EQUALS_TO_DUE_DATE;
-
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.HashMap;
@@ -53,7 +43,6 @@
 import org.hisp.dhis.api.mobile.model.DataValue;
 import org.hisp.dhis.dataelement.DataElementCategoryService;
 import org.hisp.dhis.dataelement.DataElementService;
-import org.hisp.dhis.i18n.I18n;
 import org.hisp.dhis.i18n.I18nFormat;
 import org.hisp.dhis.light.utils.NamebasedUtils;
 import org.hisp.dhis.organisationunit.OrganisationUnit;
@@ -423,8 +412,6 @@
         return validated;
     }
 
-    private I18n i18n;
-
     private I18nFormat format;
 
     private List<ProgramValidationResult> programValidationResults;
@@ -582,7 +569,7 @@
         {
             for ( ProgramValidation validation : validations )
             {
-                ProgramValidationResult validationResult = programValidationService.runValidation( validation,
+                ProgramValidationResult validationResult = programValidationService.validate( validation,
                     programStageInstance, format );
 
                 if ( validationResult != null )
@@ -591,90 +578,5 @@
                 }
             }
         }
-
-        if ( !programValidationResults.isEmpty() )
-        {
-            leftsideFormulaMap = new HashMap<Integer, String>( programValidationResults.size() );
-            rightsideFormulaMap = new HashMap<Integer, String>( programValidationResults.size() );
-
-            for ( ProgramValidationResult validationResult : programValidationResults )
-            {
-                leftsideFormulaMap.put( validationResult.getProgramValidation().getId(), programValidationService
-                    .getValidationDescription( validationResult.getProgramValidation().getLeftSide() ) );
-
-                if ( validationResult.getProgramValidation().getDateType() )
-                {
-                    String rightSide = validationResult.getProgramValidation().getRightSide();
-                    int index = rightSide.indexOf( 'D' );
-                    if ( index < 0 )
-                    {
-                        int rightValidation = Integer.parseInt( rightSide );
-
-                        switch ( rightValidation )
-                        {
-                        case BEFORE_CURRENT_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "before_current_date" ) );
-                            break;
-                        case BEFORE_OR_EQUALS_TO_CURRENT_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "before_or_equals_to_current_date" ) );
-                            break;
-                        case AFTER_CURRENT_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "after_current_date" ) );
-                            break;
-                        case AFTER_OR_EQUALS_TO_CURRENT_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "after_or_equals_to_current_date" ) );
-                            break;
-                        case BEFORE_DUE_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "before_due_date" ) );
-                            break;
-                        case BEFORE_OR_EQUALS_TO_DUE_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "before_or_equals_to_due_date" ) );
-                            break;
-                        case AFTER_DUE_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "after_due_date" ) );
-                            break;
-                        case AFTER_OR_EQUALS_TO_DUE_DATE:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(),
-                                i18n.getString( "after_or_equals_to_due_date" ) );
-                            break;
-                        default:
-                            rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), "" );
-                            break;
-
-                        }
-                    }
-                    else
-                    {
-                        int rightValidation = Integer.parseInt( rightSide.substring( 0, index ) );
-
-                        int daysValue = Integer.parseInt( rightSide.substring( index + 1, rightSide.length() ) );
-
-                        if ( rightValidation == BEFORE_DUE_DATE_PLUS_OR_MINUS_MAX_DAYS )
-                        {
-                            rightsideFormulaMap.put(
-                                validationResult.getProgramValidation().getId(),
-                                i18n.getString( "in_range_due_date_plus_or_minus" ) + " " + daysValue
-                                    + i18n.getString( "days" ) );
-                        }
-                    }
-                }
-                else if ( validationResult.getProgramValidation().getRightSide().equals( "1==1" ) )
-                {
-                    rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), "" );
-                }
-                else
-                {
-                    rightsideFormulaMap.put( validationResult.getProgramValidation().getId(), programValidationService
-                        .getValidationDescription( validationResult.getProgramValidation().getRightSide() ) );
-                }
-            }
-        }
     }
 }

=== added file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/java/org/hisp/dhis/patient/action/validation/GetProgramExpressionDescriptionAction.java'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/java/org/hisp/dhis/patient/action/validation/GetProgramExpressionDescriptionAction.java	1970-01-01 00:00:00 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/java/org/hisp/dhis/patient/action/validation/GetProgramExpressionDescriptionAction.java	2012-11-08 13:33:53 +0000
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2004-2009, 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.
+ */
+
+package org.hisp.dhis.patient.action.validation;
+
+import org.hisp.dhis.program.ProgramExpressionService;
+
+import com.opensymphony.xwork2.Action;
+
+/**
+ * @author Chau Thu Tran
+ * 
+ * @version GetProgramExpressionDescriptionAction.java 4:24:45 PM Nov 8, 2012 $
+ */
+public class GetProgramExpressionDescriptionAction
+    implements Action
+{
+    // -------------------------------------------------------------------------
+    // Dependencies
+    // -------------------------------------------------------------------------
+
+    private ProgramExpressionService programExpressionService;
+
+    public void setProgramExpressionService( ProgramExpressionService programExpressionService )
+    {
+        this.programExpressionService = programExpressionService;
+    }
+
+    // -------------------------------------------------------------------------
+    // Dependencies
+    // -------------------------------------------------------------------------
+
+    private String programExpression;
+
+    public void setProgramExpression( String programExpression )
+    {
+        this.programExpression = programExpression;
+    }
+
+    private String description;
+
+    public String getDescription()
+    {
+        return description;
+    }
+
+    // -------------------------------------------------------------------------
+    // Action implementation
+    // -------------------------------------------------------------------------
+
+    @Override
+    public String execute()
+        throws Exception
+    {
+        description = programExpressionService.getExpressionDescription( programExpression );
+
+        return SUCCESS;
+    }
+
+}

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/addSingleProgramValidationForm.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/addSingleProgramValidationForm.vm	2012-11-07 04:33:05 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/addSingleProgramValidationForm.vm	2012-11-08 13:33:53 +0000
@@ -1,4 +1,4 @@
-<h3>$i18n.getString( "add_single_program_validation_rule" )</h3>
+<h3>$i18n.getString( "create_new_program_validation_rule" )</h3>
 
 <h4>$program.name</h4>
 
@@ -17,60 +17,38 @@
 	
 	<tr>
 		<td><label>$i18n.getString('name') <em title="$i18n.getString( "required" )" class="required">*</em></label></td>
-	
 		<td>			
 			<input type="text" id='description' name='description' class="{validate:{required:true,minlength:4}}" >
 		</td>
 	</tr>
 	<tr>
-		<td>$i18n.getString("program_stage")</td>
-	
-		<td>
-			<select id='leftStage' name='leftStage' onchange="getLeftPrgramStageDataElements();" style='width:320px;'>
-				<option value=''>[$i18n.getString( "please_select" )]</option>
-				#foreach($stage in $program.programStages)
-				<option value='$stage.id'>$stage.name</option>
-				#end
-			</select>
-		</td>
-	</tr>
-	<tr>
-		<td>$i18n.getString("data_elements")</td>
-	
-		<td>
-			<select id='leftSideDE' name='leftSideDE' size='7' ondblclick="insertDataElement(this, 'leftSide', 'leftStageDescription');" style='width:320px;'>
+		<td><label for="operatorId">$encoder.htmlEncode( $i18n.getString( "operator" ) ) <em title="$i18n.getString( "required" )" class="required">*</em></label></td>
+		<td>
+			<select id="operator" name="operator">
+				<option value="">[ $i18n.getString( "select_operator" ) ]</option>
+				<option value="equal_to">$encoder.htmlEncode( $i18n.getString( "equal_to" ) )</option>
+				<option value="not_equal_to">$encoder.htmlEncode( $i18n.getString( "not_equal_to" ) )</option>
+				<option value="greater_than">$encoder.htmlEncode( $i18n.getString( "greater_than" ) )</option>
+				<option value="greater_than_or_equal_to">$encoder.htmlEncode( $i18n.getString( "greater_than_or_equal_to" ) )</option>
+				<option value="less_than">$encoder.htmlEncode( $i18n.getString( "less_than" ) )</option>
+				<option value="less_than_or_equal_to">$encoder.htmlEncode( $i18n.getString( "less_than_or_equal_to" ) )</option>
 			</select>
 		</td>
 	</tr>
 	<tr>
 		<td></td>
 		<td>
-			<input type='button' value="+" title="$i18n.getString( 'plus' )" onclick='insertOperator( "leftStageDescription", "leftSide", "+" );' class="small-button" >
-			&nbsp;&nbsp;
-			<input type='button' value="<" title="$i18n.getString( 'less_then' )" onclick='insertOperator( "leftStageDescription", "leftSide", "<" );' class="small-button" />
-			<input type='button' value="<=" title="$i18n.getString( 'less_then_or_equal_to' )" onclick='insertOperator( "leftStageDescription", "leftSide", "<=" );' class="small-button" />
-			<input type='button' value=">" title="$i18n.getString( 'greater_then' )" onclick='insertOperator( "leftStageDescription", "leftSide", ">" );' class="small-button" />
-			<input type='button' value=">=" title="$i18n.getString( 'greater_then_or_equal_to' )" onclick='insertOperator( "leftStageDescription", "leftSide", ">=" );' class="small-button" />
-			<input type='button' value="=" title="$i18n.getString( 'equals_to' )" onclick='insertOperator( "leftStageDescription", "leftSide", "==" );' class="small-button" />
-			<input type='button' value="!=" title="$i18n.getString( 'diff_from' )" onclick='insertOperator( "leftStageDescription", "leftSide", "!=" );' class="small-button"/>
-			<input type='button' value="!=NULL" title="$i18n.getString( 'not_null' )" onclick='insertOperator( "leftStageDescription", "leftSide", "=={NOT-NULL-VALUE}" );' class="nornal-button" />
-			&nbsp;&nbsp;
-			<input type='button' value="$i18n.getString( 'clear' )" title="$i18n.getString( 'clear' )" align='right'  onclick="clearValidation( 'leftSide', 'leftStageDescription' )" style="width:45px;" />
-		</td>
-	</tr>
-	<tr>	
-		<td><label>$i18n.getString("formula") <em title="$i18n.getString( "required" )" class="required">*</em></label></td>
-		<td>
-			<textarea rows='5' id='leftSide' name='leftSide' class="{validate:{required:true,minlength:4}}" onkeyup="getValidationDescription('leftStageDescription', 'leftSide')" style='width:320px;'></textarea>
-			<input type='hidden' id='rightSide' name='rightSide' value='1==1'>
-		</td>
-	</tr>
-	<tr>	
-		<td><label>$i18n.getString("description")</label></td>
-		<td>
-			<fieldset style='width:300px;'>
-				<div id='leftStageDescription' style='width:300px;'></div>
-			</fieldset>
+			<input type="button" value="$i18n.getString( 'edit_left_side' )" style="width:10em" onclick="editLeftExpression();">
+			<input type="hidden" id="leftSideDescription" name="leftSideDescription">
+			<input type="hidden" id="leftSideExpression" name="leftSideExpression">
+			<input type="hidden" id="leftSideTextualExpression" name="leftSideTextualExpression">
+			<input type="hidden" id="leftSideNullIfBlank" name="leftSideNullIfBlank">
+			
+			<input type="button" value="$i18n.getString( 'edit_right_side' )" style="width:10em" onclick="editRightExpression();"></td>
+			<input type="hidden" id="rightSideDescription" name="rightSideDescription">
+			<input type="hidden" id="rightSideExpression" name="rightSideExpression">
+			<input type="hidden" id="rightSideTextualExpression" name="rightSideTextualExpression">
+			<input type="hidden" id="rightSideNullIfBlank" name="rightSideNullIfBlank">
 		</td>
 	</tr>
 	<tr>
@@ -81,3 +59,16 @@
 		</td>
 	</tr>
 </table>
+
+#parse( "/dhis-web-maintenance-patient/expressionBuilderForm.vm" )
+
+<script>
+	hideById( "expression-container" );
+	var dialog = jQuery( "#expression-container" ).dialog({
+		modal: true,
+		autoOpen: false,
+		width: 780,
+		height: 500,
+		title: "Expression"
+	});
+</script>

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/javascript/programValidation.js'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/javascript/programValidation.js	2012-09-25 09:12:30 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/javascript/programValidation.js	2012-11-08 13:33:53 +0000
@@ -42,54 +42,13 @@
 	removeItem( programValidationId, name, i18n_confirm_delete, 'removeProgramValidation.action' );	
 }
 
-//-----------------------------------------------------------------
-// Insert items data-element
-//-----------------------------------------------------------------
-
-function insertDataElement( element, target, decriptionDiv )
-{
-	if( element.selectedIndex == -1)
-		return;
-	
-	var value = " " + element.options[element.selectedIndex].value + " ";
-	
-	insertTextCommon( target, value );
-	
-	getValidationDescription( decriptionDiv, target );
-}
-
-function insertOperator( decriptionDiv, target, value )
-{
-	insertTextCommon( target, ' ' + value + ' ' );
-	
-	getValidationDescription( decriptionDiv, target );
-}
-
-function getValidationDescription( decriptionDiv, sideDiv )
-{
-	$.postUTF8("getProgramValidationDescription.action",
-		{
-			condition: getFieldValue( sideDiv )
-		},
-		function (data)
-		{
-			setInnerHTML( decriptionDiv, data );
-		},'html');
-}
-
-function clearValidation( target, decriptionDiv )
-{
-	setFieldValue( target,'' );
-	setInnerHTML( decriptionDiv, '' );
-}
-
 //------------------------------------------------------------------------------
 // Get DataElements of Program-Stage into left-side
 //------------------------------------------------------------------------------
 
 function getLeftPrgramStageDataElements()
 {
-	clearListById( 'leftSideDE' );
+	clearListById( 'dataElementId' );
 	
 	var programStage = document.getElementById( 'leftStage' );
 	var programStageId = programStage.options[ programStage.selectedIndex ].value;
@@ -100,7 +59,7 @@
 	}, function(json){
 		for ( i in json.dataElements ) {
 			var id = '[DE:' + programStageId + '.' + json.dataElements[i].id + ']';
-			jQuery( '#leftSideDE').append( '<option value="' + id + '">' + json.dataElements[i].name + '</option>' );
+			jQuery( '#dataElementId').append( '<option value="' + id + '">' + json.dataElements[i].name + '</option>' );
 		}
 	});   
 }
@@ -244,3 +203,77 @@
 		showById('div' + dataElementId );
 	}
 }
+
+//------------------------------------------------------------------------------
+// Show Left side form for designing
+//------------------------------------------------------------------------------
+
+function editLeftExpression()
+{		
+	left = true;
+	
+	$( '#expression' ).val( $( '#leftSideExpression' ).val() );
+	$( '#description' ).val( $( '#leftSideDescription' ).val() );
+	$( '#formulaText' ).text( $( '#leftSideTextualExpression' ).val() );
+	$( '#nullIfBlank' ).attr( 'checked', ( $( '#leftSideNullIfBlank' ).val() == 'true' || $( '#leftSideNullIfBlank' ).val() == '' ) );
+	
+	dialog.dialog("open");
+}
+
+function editRightExpression()
+{
+	left = false;
+	
+	$( '#expression' ).val( $( '#rightSideExpression' ).val() );
+	$( '#description' ).val( $( '#rightSideDescription' ).val() );
+	$( '#formulaText' ).text( $( '#rightSideTextualExpression' ).val() );
+	$( '#nullIfBlank' ).attr( 'checked', ( $( '#rightSideNullIfBlank' ).val() == 'true' || $( '#rightSideNullIfBlank' ).val() == '' ) );
+	
+	dialog.dialog("open");
+}
+
+//------------------------------------------------------------------------------
+// Insert formulas
+//------------------------------------------------------------------------------
+
+function insertText( inputAreaName, inputText )
+{
+	insertTextCommon( inputAreaName, inputText );
+	
+	getExpressionText();
+}
+
+
+function getExpressionText()
+{
+	$.postUTF8("getProgramExpressionDescription.action",
+		{
+			programExpression: $( '#expression' ).val()
+		},
+		function (data)
+		{
+			setInnerHTML( "formulaText", data );
+		},'html');
+}
+
+var left = true;
+function insertExpression()
+{
+	var expression = $( '#expression' ).val();
+	var description = $( '#description' ).val();
+							
+	if ( left )
+	{
+		$( '#leftSideExpression' ).val( expression );
+		$( '#leftSideDescription' ).val( description );					
+		$( '#leftSideTextualExpression' ).val( $( '#formulaText' ).text() );
+	}
+	else
+	{
+		$( '#rightSideExpression' ).val( expression );
+		$( '#rightSideDescription' ).val( description );					
+		$( '#rightSideTextualExpression' ).val( $( '#formulaText' ).text() );
+	}
+	
+	dialog.dialog( "close" );
+}

=== modified file 'dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/programValidationList.vm'
--- dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/programValidationList.vm	2012-10-26 03:26:57 +0000
+++ dhis-2/dhis-web/dhis-web-maintenance/dhis-web-maintenance-patient/src/main/webapp/dhis-web-maintenance-patient/programValidationList.vm	2012-11-08 13:33:53 +0000
@@ -5,16 +5,28 @@
 <table class="mainPageTable">
 	<tr>
 		<td style="vertical-align:top">
-			<table class="listTable">
+			
+<table class="mainPageTable">
+  <tr>
+    <td style="vertical-align:top" colspan="2">
+		<table width="100%">
+			<tr>
+				<td>
+					$i18n.getString( "filter_by_name" ): <input type="text" onkeyup="filterValues( this.value , 1)" style="width:250px"/>
+				</td>
+				<td align="right">
+					<!-- input type="button" value="$i18n.getString( "add_multi_rule" )" onclick="window.location.href='showAddMultiProgramValidationForm.action?id=$program.id'" style="width:100px" -->
+					<input type="button" value="$i18n.getString( "back" )" onclick="window.location.href='program.action'" style="width:100px">
+					<input type="button" value="$i18n.getString( "add_new" )" onclick="window.location.href='showAddSingleProgramValidationForm.action?id=$program.id'" style="width:100px">
+				</td>
+			</tr>
+		</table>
+		<table class="listTable">
 			  <col>          
-			  <col width="60"> 
+			  <col width="100"> 
 			  <tr>
 				<td></td>
-				<td>
-					<input type="button" value="$i18n.getString( "add_single_rule" )" onclick="window.location.href='showAddSingleProgramValidationForm.action?id=$program.id'" style="width:100px">
-					<input type="button" value="$i18n.getString( "add_multi_rule" )" onclick="window.location.href='showAddMultiProgramValidationForm.action?id=$program.id'" style="width:100px">
-					<input type="button" value="$i18n.getString( "back" )" onclick="window.location.href='program.action'" style="width:100px">
-				</td>
+				<td></td>
 			  </tr>
 			  <tr>            
 				<th>$i18n.getString( "description" )</th>
@@ -38,15 +50,12 @@
 						#else
 							<a href="showUpdateMultiProgramValidationForm.action?validationId=$validation.id&id=$program.id" title="$i18n.getString( "edit" )"><img src="../images/edit.png" alt="$i18n.getString( 'edit' )"></a>
 						#end
-					  <a href="javascript:removeProgramValidation( '$validation.id', '$encoder.jsEncode( $validation.description )' )" title="$i18n.getString( "remove" )"><img src="../images/delete.png" alt="$i18n.getString( "remove" )"></a>
-					  <a href="javascript:showProgramValidationDetails( $validation.id )" title="$i18n.getString( "show_details" )"><img src="../images/information.png" alt="$i18n.getString( "show_details" )"></a>
+					  <a href="javascript:removeProgramValidation( '$validation.id', '$encoder.jsEncode( $validation.description )' )" title="$i18n.getString( "remove" )"><img src="../images/delete.png" alt="$i18n.getString( 'remove' )"></a>
+					  <a href="javascript:showProgramValidationDetails( $validation.id )" title="$i18n.getString( 'show_details' )"><img src="../images/information.png" alt="$i18n.getString( 'show_details' )"></a>
 					</td>
-					
 				  </tr>
 				#end
-
 			  </tbody>
-
 			</table>
 		</td>
 		<td style="width:20em; padding-left:2em; vertical-align:top">