GroovyForms Module


(Redirected from Modules/GroovyFormsModule)
Image:Groovy-logo.png

About the Groovy Forms Module

Mentor(s): Burke Mamlin

Assigned to: Robert O'Connor

Abstract: OpenMRS has a sophisticated data entry engine that allows administrators to define new medical concepts/questions, place them on electronic forms, and then gather data for the new concepts without any programming steps. Our existing solution depends on Microsoft's InfoPath® for both form design and data entry. While InfoPath can handle very complicated forms and has served us very well, the process of implementing new forms requires a fair amount of time and expertise. So, when new data are needed quickly, some implementations still turn to Microsoft Access® or other familiar, low-barrier systems to gather data and then they are challenged with trying to import or merge those data later. So, OpenMRS is looking for data entry alternatives. One of these alternatives could be the Groovy Forms Module. The goal of this module would be to provide an agile method for creating simple/small web forms for gathering data and using the OpenMRS API to store those data.

We are picturing a workflow allowing a OpenMRS administrator to:

  • Install the Groovy Forms Module
  • Define a simple Groovy object, from which the module auto-generates a simple web form and Groovy controller script (a Groovy script that receives the form object when submitted)
  • Be able to edit and/or apply CSS to that form through admin web pages
  • Edit a Groovy script controller for the form that could access the API.
  • Click on a button to export the new form as an OpenMRS module (.omod) file
  • Click on a button to reset the Groovy Form module and start over

For example, an administrator would use a simple web form to write a Groovy script like this:

class MyForm {
  Patient patient
  String note
}

Then clicking a button would generate a simple web page with a patient selector and a text field along with a matching controller like:

class MyFormController {
  void onSubmit(MyForm myForm) {
    // do something with myForm here
  }
}

The administrator could then edit the web page to make the note a textarea and then edit the controller to do something like this:

class MyFormController {
  void onSubmit(MyForm myForm) {
 
    // validate form
    assert myForm.patient != null "You must select a patient"
    assert myForm.note != null && myForm.note.length() > 0 "Note cannot be empty"
 
    // Add note to the repository
    obs.createObs(
      myForm.patient,
      concept.getConcept("PATIENT NOTE"),
      new Date(),
      location.getLocation(1))
  }
 
}

Target: Successful completion of this project would at the minimum allow an OpenMRS administrator to create a web-based form along with a controller to interact with the OpenMRS API after the form is submitted with minimal Groovy scripting and then "deploy" that new form all through a web browser.


Proposed Workflow

Creating a GroovyForm

  1. User goes to the admin screen and clicks on the "Manage Groovy Forms" link
  2. A screen matching existing admin management screens lists out existing groovy forms and/or provides a search box to find an existing form. There is also an "Create a new GroovyForm" link.
  3. User clicks on the link to create a new GroovyForm
  4. A "New GroovyForm" page has a form containing name, description, version number, form model, and a "Create GroovyForm" button.
    • The form model would be a textarea, possibly filled with a simple "class MyForm { // list properties here }" template
  5. After saving the GroovyForm definition, the page would be extended to contain two more textareas: one containing an auto-generated form page (as raw HTML) and the second containing a groovy script for a controller that accepts submissions

See this page for a sample workflow. Also, see this page for more information.

Design Ideas

Data Binding: Overall, most data types will be bound to text fields, with the exception of Lists -- which will be combo boxes. I will make use of ExpandoMetaClass to add functionality to Boolean instead of using a helper class to handle the selection of radio buttons or checkboxes when dealing with booleans.

class MyForm {
  Patient patient
  Boolean active
  active.checkBox = true
}

or

class MyForm {
  Patient patient
  Boolean active
  active.radio = true
}

Strings,etc pose a similar problem, only now how do we choose between text fields and text areas. I'll do a similar thing, but to the String class.


class MyForm {
  Patient patient
  String note
  note.field = true
}

or


class MyForm {
  Patient patient
  String note
  note.area = true
}

The unselected property is by default, false in this hypothetical case. The ExpandoMetaClass solution seems like a cleaner solution and a less verbose solution at that. This is applicable for both cases (String, etc and Boolean).

Front-End: See this for what it will look like. Also, see this page so that you can get a general feel for how it will behave.

Generation of the View/Controller: I will make use of a templating engine. Reflection will be used to interrogate the model to generate the form fields (see the Data Bindings item above).

Storing and Persisting the forms: the Model/View(form)/Controller will be stored in a folder whose name will be the name of the form, with spaces and illegal characters stripped all in lower-case. There will be XML metadata generated, so that the forms can be listed on the module's page within OpenMRS. The metadata will include: 1) date created 2) who created it 3) a description of the form. and 4) the name.

See this page for more information.

Current Goals (in no particular order)

See this for more information.

  • Build Groovy Form admin screen
  • Mock up sample Groovy Form module classes
  • Create a Servlet to handle rendering and submission of the forms
  • Add published forms to a list
  • Create View/Controller
    • Groovy templates

Completed Goals

  • Create Link for main admin screen
  • Build "create new form" screen
  • Create file structure
    • groovyforms
      • groovyforms.xml
      • form1
        • domain
        • view
        • controller
        • metadata.xml
  • Metadata parsing
    • Global (groovyforms.xml)
    • Individual (metadata.xml)
  • Create ability to keep the module updated when new forms are added and such
  • Groovy Model processing
    • Store fields
    • Compile to check for syntax errors and relay errors


Goals to Deliver by Midterm

Chosen Option: Option #2

Option #1

  1. Go to admin screen and find the GroovyForms section
  2. Click on a "Manage Groovy Forms" link to reach the Groovy Forms admin page
  3. Groovy Forms admin page shows existing forms as well as an "Add Groovy Form" button/link
  4. Create a new Groovy Form
  5. Enter title, version, description, and model describing a form with date and weight concept "WEIGHT (KG)"
  6. Click a button to generate view (simple web page to collect date/weight & submit) and controller with basic validation and a place for user to edit
  7. Add line to controller: obs.createObs(currentPatient, concept.getConcept("WEIGHT (KG)"), myForm.weight.value)
  8. Save the Groovy Form
  9. Publish the Groovy Form and it becomes visible on patient dashboard Forms tab
  10. Go to Find/Create Patient on patient dashboard
  11. Select Horatio Hornblower
  12. Go to forms tab and find the new Groovy Form in the list of available forms and click on it
  13. New form is presented
  14. Complete form with date and weight
  15. Submit Groovy Form
  16. Go to graphs tab on patient dashboard and see new weight

Option #2

  1. Go to admin screen and find the GroovyForms section
  2. Click on a "Manage Groovy Forms" link to reach the Groovy Forms admin page
  3. Groovy Forms admin page shows existing forms as well as an "Add Groovy Form" button/link
  4. Create a new Groovy Form
  5. Enter title, version, description, and model describing a form with birthdate
  6. Click a button to generate view (simple web page to collect date/weight & submit) and controller with basic validation and a place for user to edit
  7. Add line to controller: currentPatient.setBirthDate(myForm.birthdate)
  8. Save the Groovy Form
  9. Publish the Groovy Form and it becomes visible on patient dashboard Forms tab
  10. Go to Find/Create Patient on patient dashboard
  11. Select Horatio Hornblower
  12. Go to forms tab and find the new Groovy Form in the list of available forms and click on it
  13. New form is presented
  14. Complete form with birthdate
  15. Submit Groovy Form
  16. See new age reflected on dashboard

Project Plan

See this link for notes regarding the project. This document was used while planning and will be consulted frequently. I have chosen to allocate two-week blocks for both first and second pass. The rationale is that it allows for more flexibility in the time line. I can either scale things back or forwards.

Subject to change; will strike out as things are completed.

First Pass

Week 1 and 2: 5/27 through 6/2 up until 6/9

  • Code to generate the directory structure for each form
  • XML Parsing using xstream
    • Meta data parsing using the above mentioned library.
      • Parse global metadata (groovyforms.xml which will reside in the root directory of the groovyforms directory)
        • This will be serialized to/from a POJO (Plain Old Java Object)
      • Parse invidual groovyforms configuration files
        • Will be serialized to/from POJOS (Plain Old Java Objects)
  • Tests must be written to ensure that everything works as it should.

Week 3 and 4: 6/9 to 6/16 up until 6/23

  • GroovyForm model processing
    • Interrogate Fields via reflection and store data in a container class.
    • Ensure that it compiles
      • Relay errors if it does not.
  • Generate the Controller.
  • Generate View (final markup)
  • Ability to save the data and add the form to the system

Week 5 and 6: 6/23 to 6/30 up until 7/7

  • Write the servlet to handle form rendering and form submission
  • Write the front-end ("Create Forms", "Manage Forms", etc)

Week 7: 7/7 to 7/14(MIDTERM)

  • This final week prior to midterm will be used to polish the code from the previous 6 weeks.
  • The basework should be in place to allow for creation of at least a very simple form.

Second Pass: Post Midterm

Week 8 and 9: 7/15 to 7/21 up until 7/28

  • Allow for images in the forms
  • Add some fancy AJAX (ZK Framework)
  • Create a CSS Editor
    • May look into YUI or something similar (see this for more information.)
  • Write documentation

Week 10 and 11: 7/28 to 8/4 up until 8/11

  • If time permits
    • Create a wizard for easy creation of the forms using ZK or a similar AJAX framework.
    • Create a DSL(?)
      • May happen AFTER Summer of Code
  • Iron out rough edges
  • Present a working copy to implementors to battle test

Week 12: 8/11 to 8/18

  • Update Documentation (if needed)
  • Polish code and present to implementors with final Summer of Code Version

Future Goals

See this for more information.

  • A wizard to allow average users to generate forms using a "wizard." -- AJAX Modal window would be ultra cool.
  • a CSS Editor to edit the CSS of individual forms and possibly fields.
  • GSP integration (Grails is working to pull it out of grails and seperate it; it's on their Roadmap)
  • Groovlets (I believe this will possibly be promoted to a current goal).
  • Supplementing the patient dashboard Forms tab or adding a new tab to allow users to access these new forms
  • Supporting Groovy scripting within the form [[SECURITY HOLE]]
  • Adding AJAX capability to fields within the form
  • Restricting access to these Groovy forms by role
  • Development of domain specific language(s) to simplify the form creation or controller scripting for administrators
  • Evolving to share lessons/features from the Groovy Form Module with the existing form machinery within OpenMRS

Future Enhancements

Enhancements that can be made, in no particular order. They may not happen due to time-constraints (we only have 12 weeks to do this!).

  • Take the library of available widgets and expose them as a list of "Groovy Fields" (managed via a separate "tab" in the admin page)
    • Whether we use the ZK library or if we use some other template format, these would be pre-canned chunks of HTML (or XML) to generate our widgets (patient selector, date selector, concept selector, textarea, etc.)
    • Rather than just keeping them hidden internally to the module, we could expose the field templates (much like plugin customization in WordPress or Plone, where there's a non-editable default that be overridden by copying it to an editable field yet reset to the default with the click of a link/button)
  • Automatic support for "estimated flag" for dates -- e.g., when asking for a date within a Groovy Form, we make it simple to specify how much ambiguity is allowed and then render a widget that allows for that much ambiguity (e.g., day is optional or day & month are optional)
  • Enhance view with fancy gadgets
    • Explore ZK library as a resource for generating sophisticated form widgets
  • Add tool(s) for editing view CSS [included in the project plan, but in case it needs to pulled out]
    • Should be able to edit universal CSS (base for all Groovy Form views)
    • Should be able to edit form-specific CSS (so layout can be tweaked for a specific form)
  • Development of a Domain Specific Language to abstract away the class creation
  • Enhance view with fancy gadgets [included in the project plan, but in case it needs to be pulled out]
    • Explore ZK library as a resource for generating sophisticated form widgets
  • Prevent conflicts between Groovy Form names and OpenMRS form names

Documentation

** TO BE COMPLETED AFTER MID-TERM **

Other ideas

If you have any other ideas -- feel free to post them here. This may be my summer job, but I'd love the community's input to help guide me. -- r0bby