Groovy Module

Overview

This module was created as a proof of concept (for embedding Groovy into OpenMRS) and to serve as a base module for other modules that want to use Groovy scripting as well.

For a quick overview of some of the cool groovy features, see this spring source training video

Setup

Download and install the Groovy module omod file from the module repository.
(You can download the code from github: https://github.com/openmrs/openmrs-module-groovy)

Usage

Find and click on the Groovy Scripting Form link within the Groovy Module section of OpenMRS' administration page. You should be taken to a simple web form with a single textarea followed by a "GO" button. Type any valid Groovy script into the textarea and click the button to execute the script.

Some variables are automatically provided (most of these represent the API service):

  • admin
  • cohort
  • concept
  • encounter
  • form
  • locale
  • logic
  • obs
  • patient
  • person
  • user
  • fn (as of 2.2)

Using fn

The special fn variable provides access to convenience functions:

  • fn.sql("select count() from users")
    Runs the given SQL and returns the result set (only allows reading from database)
  • fn.sql("update users set name='Super-duper User' where user_id = 1", false)
    Runs SQL that can write to the database

Scheduling Groovy Scripts

As of version 2.2, you can schedule Groovy scripts to run automatically using the scheduler.

  1. Create and save a groovy script
  2. Create a task (Scheduler within administration page) using the Schedulable Class: org.openmrs.module.groovy.GroovyTask
  3. Add a property as follows:
    1. name: "script name" (the literal text, 'script name' without quotes... not the name of your script)
    2. value: the name of your script

Accessing Other Module Services

When trying to access the service of another module within a Groovy script, you might see a an error like:

java.lang.NoClassDefFoundError: $Proxy109

If you do, try loading the service like this:

def mymodule = Context.getService(
  Context.loadClass(
    "org.openmrs.module.mymoduleid.MyModuleService"
  )
);

Groovy script examples

List 100 concepts?

import groovy.xml.MarkupBuilder

writer = new StringWriter()
b = new MarkupBuilder(writer)

b.table(border: 1) {
  1.upto(100) {
    c = concept.getConcept(it)
    tr { td(c.name, style:"font-weight:bold"); td(c.name.description) }
  }
}
println writer

Display some encounters?

p = patient.getPatient(2)
println "<h2>${p.givenName} ${p.familyName} Encounters</h2>"
for (e in encounter.getEncounters(p)) {
  println "${String.format('%tF', e.encounterDatetime)} - ${e.creator}<br />"
  println "<ul>"
  for (o in e.getObs()) {
    println "<li>${o.concept.name}</li>"
  }
  println "</ul>"
}

Display some observations

p = patient.getPatient(2)
println "<h2>${p.givenName} ${p.familyName}</h2>"
for (o in obs.getObservations(p, false)) {
  println "${o.concept.name} : ${o.getValueAsString(locale)}<br />"
}

Search for patients by name

s = "nga"
for (p in patient.getPatientsByName(s)) {
  n = "${p.givenName} ${p.familyName}".replaceAll("(?i)($s)", "<b>\$1</b>")
  println "${n}<br />"
}

Create providers for all users

// provider service not yet provided by Groovy module (as of 2.2.1)
provider = org.openmrs.api.context.Context.getProviderService() 

// Get person IDs for all non-retired provider
allProviderPersons = provider.allProviders
  .findAll{ !it.retired && it.person } // limit to non-retired w/ person
  .collect{ it.person.personId} // just grab person IDs

// Get all non-retired users who aren't a provider
usersWithoutProviders = user.allUsers
  .findAll{
    !it.retired // non-retired
    && it.username != 'daemon' // skip daemon user
    && !allProviderPersons.contains(it.person.personId) // skip if already a provider
  }

// Create provider record for each user
for (u in usersWithoutProviders) {
  def p = new Provider()
  p.person = u.person
  person.save(p)
}

Create providers for users with specified roles

  import org.openmrs.Provider;

  targetRoles = ["Provider", "Hospital Provider", "Health Agent", "ICS Provider", "Physical Therapist", "Surgeon"]
  
  // provider service not yet provided by Groovy module (as of 2.2.1)
  provider = org.openmrs.api.context.Context.getProviderService() 
   
  // Get person IDs for all non-retired provider
  allProviderPersons = provider.allProviders
    .findAll{ !it.retired && it.person } // limit to non-retired w/ person
    .collect{ it.person.personId} // just grab person IDs
   
  // Get all non-retired users who aren't a provider
  usersWithoutProviders = user.allUsers
    .findAll{
    !it.hasRole("System Developer") && !it.retired && it.username != 'daemon' && it.username != 'ttest' &&!allProviderPersons.contains(it.person.personId) && it.roles.collect{it.name}.any{targetRoles.contains(it)}
    }
  
  // Create providers for users
  for (u in usersWithoutProviders) {
     def p = new Provider()
     p.person = u.person
     provider.saveProvider(p)
   }

Execute SQL commands

/*
 * Easy SQL example, displays number of users in database
 */

def sql(s) { admin.executeSQL(s,false) }
rs = sql("select count(*) from users")
println "Number of users ${rs}"

Manage modules

import org.openmrs.module.ModuleFactory

// Uncomment the next line to start the printing module
// ModuleFactory.startModule(ModuleFactory.getModuleById("printing"))

// list out your running modules
for (m in ModuleFactory.startedModules) {
  println "${m.name} : ${m.started}<br />"
  // uncomment the next line to stop the printing module
  // if (m.name == "Printing") ModuleFactory.stopModule(m)
}

Delete program

pid = 4

// Get program
p = program.getProgram(pid)
println "<p><b>Program ${p.programId}: ${p.name} ${p.concept}</b><br>"

program.purgeProgram(p)

Add Locations

Using groovy only:

import org.openmrs.Location

println """
<h2>Adding locations:</h2>
<table  border=1>
  <tr><th></th><th>Location name</th></tr>"""

locationNames = [ "Boucan Carre", "Belladeres", "Thomonde", "Lascahobas", "Cerca la Source", "Mirebalais", "St Marc SSPE", "POZ", "Petite Riviere", "Verrettes", "St Nicholas HSN", "Tilory", "Cerca Cavajal", "Thomassique", "Savanette", "Baptiste", "Maissade", "Dufailly"]

i = 0
locationNames.each() {
  def location = new Location()
  println "<tr><td>${++i}</td><td>${it}</td></tr>"
  location.setName("${it}")
  location.setCountry("Haiti")

ls = org.openmrs.api.context.Context.locationService
ls.saveLocation(location)
}

println  "</table>"

Using mysql:

println """
<h2>Adding locations:</h2>
<table  border=1>
  <tr><th></th><th>Location name</th></tr>"""

location = [ "Boucan Carre", "Belladeres", "Thomonde", "Lascahobas", "Cerca la Source", "Mirebalais", "St Marc SSPE", "POZ", "Petite Riviere", "Verrettes", "St Nicholas HSN", "Tilory", "Cerca Cavajal", "Thomassique", "Savanette", "Baptiste", "Maissade", "Dufailly"]

i = 0
location.each() {
  println "<tr><td>${++i}</td><td>${it}</td></tr>"
  admin.executeSQL("""
  insert into location (name, country, creator, date_created, uuid)
    values("${it}", 'Haiti', '1', now(), uuid() )""", false)
}

println  "</table>"

Print user and patients

println """
<h2>The following  people are both patients and users in your system:</h2>
<table  border=1>
<tr><th></th><th>Internal  ID</ht><th>Patient</th><th>User</th></tr>"""

i = 0
admin.executeSQL("""
select patient_id,  identifier, system_id from patient_identifier, users
where patient_id in  (select user_id from users, patient where user_id = patient_id)
and patient_id =  user_id
and not  patient_identifier.voided""", false).each  {
println  "<tr><td>${++i}</td><td>" +  it.join("</td><td>") + "</td></tr>"}
println  "</table>"

Delete duplicate patients

//List of userids with duplicate system_ids
for (uid in [20587, 22087, 18239, 18255, 18259, 18335, 18338, 18347,
18352, 18356, 18377, 18387, 18389, 18393, 18401, 18407,
18414, 18432, 18434, 18507, 17021 ] ) {

u = user.getUser(uid)
println "<h2>Original: ${u.systemId} ${u.userId} ${u.givenName} ${u.familyName}"
// Change and save system id for the first patient and print
u.setSystemId(user.generateSystemId())
user.saveUser(u,null)
println "Replaced with: ${u.systemId} ${u.userId} ${u.givenName} ${u.familyName}</h2><br>"


}

Modify observation to use coded answer

// This script will void non-coded diagnoses with 'HTA'
// and create a new obs with a coded diagnosis for Hypertension
def reason = 'Migrate non-coded HTA to coded + '

// Set the varibles
// Non-coded concept diagnosis
def oldConceptId = 6543
// Coded concept for diagnosis
def newConceptId = 6542
// Coded answer for hypertension
def codedId = 903
// Location is Lacolline only
def locationId = 7
// Non-coded value_text
def diagnosis = "HTA"

c = concept.getConcept(newConceptId)
vc = concept.getConcept(codedId)
oc = concept.getConcept(oldConceptId)
l = location.getLocation(locationId)

// Loop thru all the obs where concept_id is for non-coded diagnosis, one location, and not voided
for (o in obs.getObservations(null,null,[oc],null,null,[l],null,null,null,null,null,false)) {

    // If the value_text is like the non-coded, print and change it
    if (o.valueText.toUpperCase() ==~ diagnosis ) {
      println "${o.concept.name}: ${o.valueText}<br />"

      // Void the non-coded obs; create coded concepts
      o.setConcept(c);
      o.setValueCoded(vc);
      o.setValueText(null);
      obs.saveObs(o,reason);
    }
}

Delete observations

/ Delete observation
p = patient.getPatient(15919)
e = encounter.getEncounter(39696)
// Print name and location for patient
println "<h2>${p.givenName} ${p.familyName} ${e.location}</h2>"

// print information about the observations
for (o in obs.getObservations(p,false)) {
if (o.location =\~ 'Rwaza') {
println "${o.location} ${o.obsId} ${o.concept.name} : ${o.getValueAsString(locale)}<br/>"
}
}

// obs_id for location 33 (Rwaza)
def numbers =

// get observations and purge them
for (obsno in numbers) {
thisObs = obs.getObs(obsno)
println "${thisObs.location} ${thisObs.obsId} ${thisObs.concept.name} ${thisObs.getValueAsString(locale)}<br />"
obs.purgeObs(thisObs)


}

Delete concept and concept answers

conceptID = 3823


c = concept.getConcept(conceptID);
println "<h2>${c.conceptId} ${c.name}</h2>"
for (ca in c.getAnswers()) {
println "<h2>${ca.concept.conceptId} ${ca.conceptAnswerId} ${ca.answerConcept} </h2>"

//  This works, but isn't saved
c.removeAnswer(ca)
}

//This doesn't save the removed answers
concept.saveConcept(c)

// This proves that it is removed
println "Second pass\n"
for (ca2 in c.getAnswers()) {
println "<h2>${ca2.concept.conceptId} ${ca2.conceptAnswerId} ${ca2.answerConcept} </h2>"
}

// This won't work until the answers are deleted
//concept.deleteConcept(concept.getConcept(conceptID))

Change program and workflow

// What are the program_id and workflow_id?
//  Get these from the GUI
//  IMPORTANT:  These variables are real for Rwanda and HEART FAILURE PROGRAM and DIAGNOSIS WORKFLOW
//  Otherwise...replace with the real numbers
pid = 11
wid = 21
// Get program
p = program.getProgram(pid)
println "<p><b>Program ${p.programId}: ${p.name} ${p.concept}</b><br>"

// Get program workflow
pw = program.getWorkflow(wid)
println "<p><b>Workflow ${pw.programWorkflowId}: ${pw.concept} ${pw.concept.name}</b> ${pw.program.programId} <br>${pw.program}</p>"

//  Get all states and delete the states for the appropriate workflows
for (s in program.getStates()) {
println "<p><b>States ${s.programWorkflowStateId}: ${s.concept} ${s.concept.name}</b><br>${s.programWorkflow} </p>"
pw.removeState(s)
}

//  Save the removed workflow and save the program
//  NOTE: Uncomment the next 2 lines after testing
//p.removeWorkflow(pw)
//program.saveProgram(p)

Change person

// Change the Given name for a Person

// Variables
person_id = 20754
new_given_name = "MBASABYAMAHORO"
new_birthdate = "1980-01-01 00:00:00.0"

// Second patient
//person_id = 1588
//new_given_name = "Claudine"
//new_family_name = "MUKARURANGA"

// Get person
p = person.getPerson(person_id)
name = p.getNames()
bd = p.getBirthdate()

// Print out current values
given_name = p.getGivenName()
family_name = p.getFamilyName()
println "<p>Original given name: ${given_name}</br>"
println "<p>Original family name: ${family_name}</br>"
println "<p>Birthdate: ${bd}</br>"

// Get person name
pn = p.getPersonName()
println "<p>Person name: ${pn}</br>"
pn.setGivenName(new_given_name)
//pn.setFamilyName(new_family_name)

// Add and save the corrected name
p.addName()
person.savePerson(p)

// Print the corrected values
cname = p.getNames()
println "<h3>Corrected name:  ${cname}</h3></br >"

Or this simple script

import org.openmrs.PersonName
p = person.getPerson(1);
p.addName(new PersonName("Given", "middle name", "Family"));
person.savePerson(p);

Change form id

This used when we duplicate an InfoPath form into htmlform, and want to change all the infopath forms to use htmlform view/edit capabilities.

// Original formId of Infopath form
infopathFormId = 82

// formId of htmlform form
htmlformId = 188

f = form.getForm(infopathFormId)
htmlform = form.getForm(htmlformId)

for (e in encounter.getEncounters(null,null,null,null,[f],null,null,true)) {
   if (e.getForm().getFormId() == infopathFormId) {
       e.setForm(htmlform)
   }
   encounter.saveEncounter(e)
}

Retire form

// Set variables
form_id = 99
user_id = 7
reason = 'Form no longer needed'
def today= new Date()

u = user.getUser(user_id)

f = form.getForm(form_id)

// Print form information
println "<h2>Old Form: ${f} ${f.name}"
println " ${f.retired} ${f.retiredBy} ${f.retiredReason} ${f.dateRetired} "

// Set form to retired
f.setRetired(true)
f.setRetiredBy(u)
f.setRetiredReason(reason)
f.setDateRetired(today)

form.saveForm(f)

// Print form information
println "Saved Form: ${f} ${f.name}"
println " ${f.retired} ${f.retiredBy} ${f.retiredReason} ${f.dateRetired} </h2>"

Delete forms (with no encounters)

import org.openmrs.FormField

// delete all formfield rows, but need to null the parent first
for (ff in form.getAllFormFields() ) {
  // set parent_form_field to NULL
  ff.setParent(null)
  form.saveFormField(ff) }

for (ff2 in form.getAllFormFields() ) {
  form.deleteFormField(ff2) }

for ( form_id in [9,10,11,12] ) {
  // get form_id
  f = form.getForm(form_id)

  // delete the formentry_xsn rows  // This next line requires a change to the groovy module (see GRV-18)
  formEntry.deleteFormEntryXsn(f)

  // Print form information
  println "<h2>deleting form: ${f} ${f.name}"

  // delete each form
  form.deleteForm(f)
}

Retire drug

// Set variables
drug_id = 11
user_id = 7
reason = 'test retire'
def today= new Date()

u = user.getUser(user_id)
d = concept.getDrug(drug_id)

// Print drug information
println "<h2>Old drug: ${d} ${d.name}"
println " ${d.retired} ${d.retiredBy} ${d.retireReason} ${d.dateRetired} </h2>"

// Set form to retired
d.setRetired(true)
d.setRetiredBy(u)
d.setRetireReason(reason)
d.setDateRetired(today)
concept.saveDrug(d)

Remove birthdates

This is useful when no birthdate is known and bogus ages are selected (ie. 106 years old)

// remove bogus birthdates

for (person_id in [4,5] ) {

  // Get person
  p = person.getPerson(person_id)

  // Print out current values
  bd = p.getBirthdate()
  pn = p.getPersonName()
  println "<p>Person name: ${pn}  Birthdate: ${bd}</br>"

  // Remove birthdate
  p.setBirthdate(null)
  p.setBirthdateEstimated(false)
  person.savePerson(p)
}

Print concept sets and names 

set = concept.getConcept(3642);
for (sm in set.conceptSets) {
    println(sm.conceptSetId + " , " + sm.concept.conceptId + " , " 
                 + sm.conceptSet.conceptId + " , " + sm.sortWeight + " , "
                 + sm.dateCreated + " , " + sm.uuid);
    println(" = ");
    println(sm.concept.names);
    println(" "); 
}

Void concept names

// Void these concept_names
def conceptNameIds = [13932, 13662, 13778,13670,7105,11259,13310, 13935, 13731, 13460,13284,10000,9860];

def reason = 'Duplicate name removed for OpenMRS 1.9'
def today= new Date()
def user_id = 1010

u = user.getUser(user_id)

for (conceptNameId in conceptNameIds) {

    org.openmrs.ConceptName name = concept.getConceptName(conceptNameId);
    org.openmrs.Concept c = name.getConcept()

    name.setVoided(true);
    name.setVoidedBy(u);
    name.setDateVoided(today);
    name.setVoidReason(reason);

    println "<h2>${name.name} </h2>"

    concept.saveConcept(c);
}

Change ConceptSource

 

def sourceName = 'IMB Rwanda' 
def newName = 'Rwanda' 
def newCode = '' 
def newDesc = 'Rwanda harmonized concepts for MoH and IMB'

source = org.openmrs.api.context.Context.getConceptService().getConceptSourceByName(sourceName); 
println "<p><b>Name: ${source.getConceptSourceId()} ${source.getName()}</b><br>"   

source.setName(newName) 
source.setHl7Code(newCode) 
source.setDescription(newDesc)

Change concept_map comment (for OpenMRS 1.9 upgrade)

def oldTypeName = "Map Type: 3";
def typeName = "Map Type: BROADER-THAN";
def sourceNames = ["ICD-10","SNOMED NP","ICD-10-WHO NP", "ICD-10-WHO 2nd"];
for (sourceName in sourceNames) {
    org.openmrs.ConceptSource source = concept.getConceptSourceByName(sourceName);
    List conceptMaps = concept.getConceptsByConceptSource(source);
    for (conceptMap in conceptMaps) {
      if (oldTypeName.equals(conceptMap.getComment())) {
        println "<p><b>Map: ${conceptMap.concept}: ${conceptMap.comment}</b><br>"
        conceptMap.setComment(typeName);
        concept.saveConcept(conceptMap.getConcept());
      }
    }
}
def typeName = "Map Type: SAME-AS";
def sourceNames = ["PIH","org.openmrs.module.mdrtb",
     "local","AMPATH","ZL/PIH installation in Haiti",
     "ZL/PIH installation in Haiti - Boston server",
     "MVP/CIEL at PIH"];
for (sourceName in sourceNames) {
    org.openmrs.ConceptSource source = concept.getConceptSourceByName(sourceName);
    List conceptMaps = concept.getConceptsByConceptSource(source);
    for (conceptMap in conceptMaps) {
      if (!typeName.equals(conceptMap.getComment())) {
        println "<p><b>Map: ${conceptMap.concept}: ${conceptMap.comment}</b><br>"
        conceptMap.setComment(typeName);
        concept.saveConcept(conceptMap.getConcept());
      }
    }
}

Remove role

roleName = "Patient Registration"
us = org.openmrs.api.context.Context.userService
r = us.getRole(roleName)
println "<p><b>Role: ${r.role}<br>description: ${r.description}<br> uuid: ${r.uuid}</b><br>"
us.purgeRole(r)

A more complex example

/\*
* For adult return visits within two clinic modules during November 2008, examine the CD4 counts
* available to the clinician at the time of the encounter, calculate several decision support reminder
* triggers, and then output -- as comma-separated values -- each visit along with whether or not each
* reminder would have been triggered, whether the clinician ordered a CD4 count at the visit, and whether
* or not a CD4 was obtained during the visit (or within the following week).  Include all known CD4 counts
* the the patient at the end of each line for quick validation.
\*/
import org.openmrs.Obs
import org.openmrs.Person
import org.openmrs.Encounter

// Easy SQL calls
def sql(s) { admin.executeSQL(s,false) }

// Desired date format
def df = new java.text.SimpleDateFormat('MM/dd/yyyy')

// Used to compare observations -- e.g., to find max/min
def byValue = \[compare:{ a, b -> a.valueNumeric <=> b.valueNumeric }\] as Comparator
def byDate = \[compare:{ a, b -> a.obsDatetime <=> b.obsDatetime }\] as Comparator

// Some magic Groovy Obs methods
Obs.metaClass.'asOf' = { Date d -> delegate.obsDatetime < d }
Obs.metaClass.'above' = { value -> delegate.valueNumeric > value }
Obs.metaClass.'below' = { value -> delegate.valueNumeric < value }
Obs.metaClass.'text' = { "${delegate.valueNumeric} on ${df.format(delegate.obsDatetime)}" }

// Add some magic to lists (ArrayList is Groovy's default for lists) for convenience
ArrayList.metaClass.'asOf' = { Date d \-> delegate.findAll{ it.asOf(d) }}
ArrayList.metaClass.'text' = { delegate.collect{it.text()} }

// More Groovy fun: checking list sizes in a way a doctor might understand -- e.g., 2.of(this) && 3.orMore(that)
Integer.metaClass.'of' = { list -> list != null && list.size() == delegate }
Integer.metaClass.'orMore' = { list -> list != null && list.size() >= delegate }

// And why not make it easy to lookup CD4s or identifiers using internal IDs while we're enjoying Groovy magic
def CD4_CONCEPT = concept.getConcept(5497) // Our CD4 concept
Integer.metaClass.'cd4s' = { obs.getObservationsByPersonAndConcept(new Person(delegate),CD4_CONCEPT) }
Integer.metaClass.'identifiers' = { patient.getPatient(delegate).identifiers.collect{ it.identifier } }

// Fetch names of target locations
targetLocations = [13, 14] // 13 = Module 2, 14 = Module 3
location = org.openmrs.api.context.Context.locationService
locationName = []; targetLocations.each { locationName[it] = location.getLocation(it).name }

// Methods to check for orders or results reported within an encounter
def TESTS_ORDERED = concept.getConcept(1271) // Our TEST ORDERED concept
def CD4_PANEL = concept.getConcept(657) // Our CD4 PANEL concept (answer to TESTS ORDERED if doctor ordered a CD4)
// Return true if CD4 ordered within encounter for patient
def cd4Ordered = { pid, encId \->
1.orMore(obs.getObservations([new Person(pid)|new Person(pid)],[new Encounter(encId)|new Encounter(encId)],[TESTS_ORDERED|TESTS_ORDERED],
[CD4_PANEL|CD4_PANEL],null,null,null,null,null,null,null,false))
}
// Return true if CD4 result within one week of encounter date
def cd4Obtained = { pid, encDate \->
1.orMore(obs.getObservations([new Person(pid)|new Person(pid)],null,[CD4_CONCEPT|CD4_CONCEPT],
null,null,null,null,null,null,encDate-1,encDate+7,false))
}

// Our reminders
def reminders = \[
// No prior CD4
1 : { encDate, cd4s -> cd4s == null || 0.of(cd4s) },

// Only one CD4 over 6 months ago
// TODO: need to match Brian's logic for what "6 months" means
2 : { encDate, cd4s -> 1.of(cd4s) && cd4s[0].asOf(encDate-180) },

// Only two prior CD4s, at least one < 400, both over 6 months ago
3 : { encDate, cd4s -> 2.of(cd4s) && cd4s.min(byValue).below(400) && cd4s.max(byDate).asOf(encDate-180) },

// More than two prior CD4s, latest < 400 and over 6 months ago
4 : { encDate, cd4s -> 3.orMore(cd4s) && cd4s.max(byDate).below(400) && cd4s.max(byDate).asOf(encDate-180) },

// More than two prior CD4s (or only two if both >400) with most recent over 12 months ago and >400
5 : { encDate, cd4s -> (3.orMore(cd4s) || (2.of(cd4s) && cd4s.min(byValue).above(400)))
                           && cd4s.max(byDate).asOf(encDate - 365) }

\] // end of reminders

// Select adult return visit encounters at our target locations between 3-Nov-2008 and 28-Nov-2008
rs = sql("""
select patient_id, encounter_id, encounter_datetime, location_id
from encounter
where encounter_datetime >= '2008-11-03' and encounter_datetime < '2008-11-29'
and encounter_type = 2 /\* ADULTRETURN encounter type \*/
and location_id in (${targetLocations.join(',')})
""")

// Loop through the encounters, creating a CSV output
println "Date,Patient,pid,Encounter,Module,${reminders.keySet().join(',')},Any,Ordered,Obtained,CD4s"
rs.each { row \->
(pid, encId, encDate, locationId) = row
identifier = pid.identifiers()[0] // first patient identifier is preferred
module = locationName[locationId] // the clinic module
allCd4s = pid.cd4s()               // all known CD4 counts for the patient
cd4s = allCd4s.asOf(encDate)       // only CD4s prior to the current encounter
ordered = cd4Ordered(pid, encId) ? 'Y' : 'N'
obtained = cd4Obtained(pid, encDate) ? 'Y' : 'N'
reminderResults = reminders.collect { n, fn -> fn(encDate,cd4s) ? 1 : 0 } // one line runs our reminders
anyReminder = reminderResults.sum() > 0 ? 1 : 0

// output in CSV format
print "${df.format(encDate)},$identifier,$pid,$encId,$module,${reminderResults.join(',')},$anyReminder,"
print """$ordered,$obtained,"${allCd4s.sort(byDate).reverse().text().join(',')}"\n"""
}

Trigger Sync for a Patient (may not be complete)

import java.util.*;
import org.openmrs.*;
import org.openmrs.api.*;
import org.openmrs.api.context.Context;

String patientUuid = null;

Date now = new Date();

if (patientUuid != null) {
     p = patient.getPatientByUuid(patientUuid);
     p.setDateChanged(now);

     for (pn in p.names) {
         pn.setDateChanged(now);
     }

     for (pa in p.addresses) {
         pa.setDateChanged(now);
     }

     for (pa in p.attributes) {
         pa.setDateChanged(now);
     }

     for (pi in p.identifiers) {
         pi.setDateChanged(now);
     }

     patient.savePatient(p);
     println "Saved Patient " + p;

     for (r in person.getRelationshipsByPerson(p)) {
         r.setDateChanged(now);
         person.saveRelationship(r);
         println "Saved relationship " + r;
     }

     for (e in encounter.getEncountersByPatient(p)) {
         e.setDateChanged(now);
         if (e.visit != null) {
              e.visit.setDateChanged(now);
         }
         for (o in e.orders) {
            o.setDateChanged(now);
         }
         for (o in e.obs) {
            o.setDateChanged(now);
         }

         for (ep in e.encounterProviders) {
              ep.setDateChanged(now);
              if (ep.encounterRole != null) {
                  ep.encounterRole.setDateChanged(now);
              }
         }
         encounter.saveEncounter(e);
         println "Saved encounter " + e;
     }

    visitService = Context.getService(VisitService.class);
    for (v in visitService.getVisitsByPatient(p)) {
        v.setDateChanged(now);
        visitService.saveVisit(v);
        println "Saved visit " + v;
    }

    for (o in order.getDrugOrdersByPatient(p, OrderService.ORDER_STATUS.ANY, true)) {
        Date dateCreated = o.getDateCreated();
        Calendar cal = Calendar.getInstance();
        cal.setTime(dateCreated);
        cal.add(Calendar.SECOND, 1);
        o.setDateCreated(cal.getTime());
        order.saveOrder(o);
        println "Saved drug order " + o;
    }

    for (o in obs.getObservationsByPerson(p)) {
        if (o.encounter == null) {
            Date dateCreated = o.getDateCreated();
            Calendar cal = Calendar.getInstance();
            cal.setTime(dateCreated);
            cal.add(Calendar.SECOND, 1);
            o.setDateCreated(cal.getTime());
            obs.saveObs(o, 'Resaving patient and all data for sync');
            println "Saved obs " + o;
        }
    }

     for (pp in program.getPatientPrograms(p, null, null, null, null, null, true)) {
         pp.setDateChanged(now);
         for (ps in pp.states) {
              ps.setDateChanged(now);
         }
         program.savePatientProgram(pp);
         println "Saved patient program " + pp.id;
     }
}
else {
    println "You need to enter a patientUuid at the top of the script to use it"
}

Make REST web service calls

Make sure you don't save your password in a script! You can enter your password into this script and run it, but make sure to remove your password before saving the script!

The following script sets up basic authentication, makes a call to the REST Web Services module, and then displays the result.

Copy the JSON-formatted result into an editor like Sublime Text 2 with a JSON formatter (in Sublime Text 2, Tools > Command Palette > Package Control : Install Package > Pretty JSON) to quickly format the JSON into a more legible form.

username = "admin"
password = "test"
Authenticator.setDefault (new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication (username, password.toCharArray());
    }
});
new URL("http://localhost:8081/openmrs-standalone/ws/rest/v1/person?q=darius")
  .text.replaceAll(",",",\n")

Look for HTML Forms that have the same concept repeated, without extra contextual information

import org.openmrs.api.context.Context

// 6042 is free-text diagnosis
def ignoreWarningsFor = [ "6042" ]

def loadInstance = { className ->
    return Context.loadClass(className).newInstance()
}

def HtmlFormEntryUtil = loadInstance("org.openmrs.module.htmlformentry.HtmlFormEntryUtil")
def FormEntryContext = loadInstance("org.openmrs.module.htmlformentry.FormEntryContext")

def FormEntrySessionClass = Context.loadClass("org.openmrs.module.htmlformentry.FormEntrySession")
def HtmlFormClass = Context.loadClass("org.openmrs.module.htmlformentry.HtmlForm")

def service = Context.getService(Context.loadClass("org.openmrs.module.htmlformentry.HtmlFormEntryService"))

def getSchema = { hf ->
    def constructor = FormEntrySessionClass.constructors.find {
        def helper = it.parameterTypes.collect { it.name }.join(",")
        return helper == "org.openmrs.Patient,org.openmrs.module.htmlformentry.HtmlForm,javax.servlet.http.HttpSession"
    }
    def fes = constructor.newInstance(HtmlFormEntryUtil.getFakePerson(), hf, null);
    return fes.getContext().getSchema();
}

def obsFieldSignature = { obsField ->
    def temp = "${ obsField.question.id }"
    if (obsField.answers) {
        temp += "(" + obsField.answers.collect { it.concept.id } .join(", ") + ")"
    }
    return temp;
}

def checkForUnsafeObsFields = { prefix, fields ->
    println("(debug) looking at ${ prefix }")
    def questionsUsed = new TreeSet();
    fields.each {
        if (it.class.simpleName == "ObsField") {
            def obsFieldSig = obsFieldSignature(it)
            if (questionsUsed.contains(obsFieldSig) && !ignoreWarningsFor.contains(obsFieldSig)) {
                println("${ prefix } -> Warning about ${ obsFieldSig }")
            }
            questionsUsed.add(obsFieldSig)
        }
    }
}

def checkField
checkField = { prefix, field ->
    def test = field.class.simpleName
    if ("HtmlFormSection" == test) {
        def name = "${prefix} -> (Section) ${field.name} "
        checkForUnsafeObsFields(name, field.fields)
        field.fields.each {
            checkField(name, it)
        }
    } else if ("ObsGroup" == test) {
        def name = "${prefix} -> (ObsGroup) ${field.concept.name.name} "
        checkForUnsafeObsFields(name, field.children)
        field.children.each {
            checkField(name, it)
        }
    }
}

def checkFields = { prefix, fields ->
    fields.each {
        checkField(prefix, it)
    }
}

service.allHtmlForms.each { hf ->
    println("=== ${ hf.name } ===")
    def schema = getSchema(hf)
    println("Schema has ${ schema.allFields.size() } fields")
    checkFields("  ", schema.allFields)
}

"Done."

Update a Person Attribute and insert an Obs given a value from another Obs

 

/**********
If  concepts
 celular del paciente que dio el paciente o celular modificado  are 
later than latest changed by date for cel attribute
* place them in the celular history and then in the patient attribute
********/

  import org.openmrs.api.context.Context
    import org.openmrs.api.EncounterService;
    import org.openmrs.module.groovy.GroovyUtil
    import org.openmrs.Encounter;
    import java.lang.Integer;
    import org.openmrs.EncounterType;
    import org.openmrs.Person;
    import org.openmrs.Patient;
    import org.openmrs.Obs;
    import org.openmrs.PersonAttribute;
    import org.openmrs.Concept;
    import org.hibernate.SessionFactory;
    import org.openmrs.api.FormService;
    import org.openmrs.api.ObsService;
    import org.openmrs.api.ConceptService;
    import org.openmrs.api.PersonService;
    import org.openmrs.Location;
    
    sf = Context.serviceContext.applicationContext.getBean(SessionFactory.class)
    ObsService os = Context.getObsService()
    ConceptService cs = Context.getConceptService();
    PersonService ps = Context.getPersonService();
    
def sql(s) { admin.executeSQL(s,false) }

rs = sql("""
select p.patient_id as pid,REPLACE(FORMAT(o.value_numeric, 0), ',', '') as celular from patient p, obs o, person_attribute pa where p.patient_id = o.person_id and p.patient_id = pa.person_id and pa.person_attribute_type_id = 8 and o.concept_id in (331,138) and o.voided=0 and p.voided=0 and pa.voided=0 and IF(pa.date_changed is not null, pa.date_changed, pa.date_created) < o.obs_datetime and pa.value != o.value_numeric;
""")

rs.each { row ->
          (pid,celular) = row
       Obs o = new Obs();
print celular
       o.setConcept(cs.getConcept(260));
       o.setDateCreated(new Date());
       o.setCreator(Context.getAuthenticatedUser());
       o.setLocation(new Location(1));
       o.setObsDatetime(new Date());
       o.setPerson(new Person(pid));
       o.setValueText(celular.toString());
       os.saveObs(o,"inserted by groovy script");
      
       PersonAttribute pa = new PersonAttribute();
       def t = ps.getPersonAttributeType(new Integer(8));
       pa.setAttributeType(t);

       def p = ps.getPerson(new Patient(pid));
       pa.setPerson(new Person(pid));
       pa.setValue(celular.toString());
       pa.setDateChanged(new Date());
       p.addAttribute(pa);
       ps.savePerson(p);

     //print e
}

A slightly groovier way

/**********
If  concepts
 celular del paciente que dio el paciente o celular modificado  are 
later than latest changed by date for cel attribute
* place them in the celular history and then in the patient attribute
********/

  import org.openmrs.api.context.Context
    import org.openmrs.api.EncounterService;
    import org.openmrs.module.groovy.GroovyUtil
    import org.openmrs.Encounter;
    import java.lang.Integer;
    import org.openmrs.EncounterType;
    import org.openmrs.Person;
    import org.openmrs.Patient;
    import org.openmrs.Obs;
    import org.openmrs.PersonAttribute;
    import org.openmrs.Concept;
    import org.hibernate.SessionFactory;
    import org.openmrs.api.FormService;
    import org.openmrs.api.ObsService;
    import org.openmrs.api.ConceptService;
    import org.openmrs.api.PersonService;
    import org.openmrs.Location;
    
    sf = Context.serviceContext.applicationContext.getBean(SessionFactory.class)
    ObsService os = Context.getObsService()
    ConceptService cs = Context.getConceptService();
    PersonService ps = Context.getPersonService();
    
def sql(s) { admin.executeSQL(s,false) }

rs = sql("""
select p.patient_id as pid,REPLACE(FORMAT(o.value_numeric, 0), ',', '') as celular from patient p, obs o, person_attribute pa where p.patient_id = o.person_id and p.patient_id = pa.person_id and pa.person_attribute_type_id = 8 and o.concept_id in (331,138) and o.voided=0 and p.voided=0 and pa.voided=0 and IF(pa.date_changed is not null, pa.date_changed, pa.date_created) < o.obs_datetime and pa.value != o.value_numeric;
""")

rs.each { row ->
  (pid,celular) = row

  def o = new Obs()
  def pa = new PersonAttribute()
  o.concept = concept.getConcept(260)
  o.dataCreated = new Date()
  o.creator = Context.authenticatedUser
  o.location = new Location(1)
  o.obsDatetime = new Date()
  o.person = new Patient(pid)
  o.encounter = new Encounter(enc_id)
  o.valueText = celular
  obs.saveObs(o,"inserted by groovy script")
  
  pa.person = new Patient(pid)
  pa.attributeType = person.getPersonAttributeType(8)
  pa.value = celular
  pa.dateChanged = new Date()
}

Troubleshooting

java.lang.ClassFormatError: Invalid method Code length 82342 in class file Script1

Solution: Java only allows methods that are 64K long. Reduce the size of your groovy script.