User-defined Rules

Starting with version 0.5 of the Logic Module, users may define rules, which are saved in the database. The logic module has built-in support for writing rules in Groovy and Java, while other modules may add other languages by overriding the org.openmrs.logic.rule.definition.LanguageHandler interface.

At present you have to look in your server logs to see compile errors if you've written a rule incorrectly.

Groovy Rules

When defining a Groovy rule, you are defining the contents of the rule's eval method. Precisely, the code you are writing is enclosed in 

import java.util.Map;
import org.openmrs.Patient;
import org.openmrs.logic.*;
import org.openmrs.logic.rule.*;
import org.openmrs.logic.result.*;

public class GENERATED-NAME extends AbstractRule {
    public Result eval(LogicContext context, Integer patientId, 
            Map<String, Object> parameters) throws LogicException {
        // YOUR-CONTENT-INSERTED-HERE
    }
}

You have access to all of Groovy's language features, including closures, so as a very contrived example you could do something like this:

def square = { x ->
    x * x
}

def myString = [2, 4, 6].collect { "${ it } squared is ${ square(it) }" }.join("\n")

return new Result(myString)

A more useful rule, to fetch the patient's age, would be:

return new Result(context.getPatient(patientId).getAge(context.indexDate))

Here's a verbose version of that same age logic

/*
 * In order to add as little overhead as possible, allowing rules to be run 
 * relatively efficiently on very large cohorts, the rule framework does not 
 * automatically provide us with a Patient object, but just a patientId.
 * This line fetches that Patient object. (It actually fetches all patients 
 * in the cohort the rule is being evaluated on.)
 */
def patient = context.getPatient(patientId)

/*
 * We want this rule to work correctly if you evaluate it as-of some other 
 * date. So we fetch the index date from the LogicContext. "context.indexDate" 
 * is groovy shorthand for "context.getIndexDate()"
 */
def age = patient.getAge(context.indexDate)

/*
 * We need to wrap our returned value in a logic Result.
 */
return new Result(age)

Java Rules

These "work", but are very hard to use. A later logic module release will make these easier to use. (But why not use Groovy instead? It's better...)

Your rule definition must be a complete Java class definition, in the package "org.openmrs.module.logic.rule" with the class name "CompiledRuleXYZ" where XYZ is the internal ID of the rule definition (which you can read from the url of the Edit Rule Definition page after "id=").

For example, the following would work (if it is the rule with id=3):

package org.openmrs.module.logic.rule;

import java.util.Map;
import org.openmrs.Patient;
import org.openmrs.logic.*;
import org.openmrs.logic.rule.*;
import org.openmrs.logic.result.*;

public class CompiledRule3 extends AbstractRule {
    public Result eval(LogicContext context, Integer patientId, 
            Map<String, Object> parameters) {
        return new Result("I would recommend Groovy instead");
    }
}