It is with great pleasure that I announce the release of version 1.0 of the Facility Data Module.
It is available for downloaded via the OpenMRS module repository.
Feedback is appreciated!
It's been a while since I last updated on the progress of the Facility Data Module. So without farther adieu here we go:
- The relation between the FacilityDataFormSchema and FacilityDataFormSection is now Many to Many meaning that sections can now be re-used between several form schemas (or reports).
- It now runs without errors on OpenMRS 1.5.x
What's next on my list is the following:
- Allow for sections to be re-ordered within the schema (this is currently not possible and is a show-stopper.
- Fix up the calendar management pages -- those are horribly ugly, but this is after all a first pass, so that is to be expected right?
- Currently, it does not validate inputs for Numeric data types -- It need to.
- There still needs to be the ability to analyze the data that exists in the system to submit to funding sources; those pages need to be implemented but that is phase two of the project.
- There are likely other sore spots that exist that I do not see at this point and time that will be fixed at a later date.
I have made a lot of progress on my Google Summer of Code Project.
First, I've completed the code for the generation of the directory structure for the forms. This is done using the form id, which is the name with all illegal characters removed. The only valid characters left are alpha-numeric and periods. Let me tell you, the regular expression to replace all of those characters was hell to write. But, thanks to the help of Jason Davis, I was able to make it much more readable and easier digest. You've gotta see it to get an idea of how bad it was, so here goes:
/**
* Removes all illegal characters, then removes all spaces and makes it all lower-case.
*
* @param str the form name
* @return a viable form id.
*/
public static String formNameToFormId(String str) {
return str.replaceAll("[\\$\\(\\)%`~!@#&;:\\^=\\+\\?
\"\\*\\\\////\\\\{\\}'\\]\\[\\|\\s+]", "").toLowerCase();
}
That is pretty much hell on earth to read, and it was hell on earth to type. Here's the *MUCH* simpler version:
/**
* Removes all illegal characters, then removes all spaces and makes it all lower-case.
*
* @param str the form name
* @return a viable form id.
*/
public static String formNameToFormId(String str) {
return str.replaceAll("[^a-zA-Z0-9.]", "").toLowerCase();
}
Note, you can clearly see that i'm negating that entire sequence. It does the SAME thing, but is MUCH more readable. That reads: "replace everything that isn't a letter from A to Z *OR* a to z(lower case letters) *OR* from 0 through 9 *OR* a period. Whereas the above just excluded the invalid characters explicitly but was a MONSTER of a regular expression.
Secondly, metadata persistence is written in, I'm using xstream. It's a great tool for XML serialization.
That's it. Oh, by the way, these are the things I earmarked TWO WEEKS FOR! Now, I push domain class model processing up, will work on that tomorrow.
Okay, week 1 is nearing an end.I accomplished alot of things.
First, I completed the meta-data storage code. I wrote some tests, which revealed some bugs that needed fixing, and they were. I am happy to say, that the metadata storage works!
Writing the test was difficult due to the fact I store the forms in the system using a static List in my container class. I wound up updating to JUnit 4 to get at the @BeforeClass annotation so that the only one set of forms gets loaded into the container class. This helped me greatly. Additionally, the @AfterClass annotation was handy for cleaning up from the tests.
Secondly, Writing the code for interrogating the model was a piece of cake, thanks to groovy. All code for interrogating the model has 50 lines! That includes a model class I wrote to hold the field types and field names. I'll show you, but before I do, I should note that return statements are optional, and the final statement will be returned.
package org.openmrs.module.groovyforms.util
import java.lang.reflect.Field
import org.openmrs.module.groovyforms.metadata.model.GroovyFormsDomainModel
/*
* Utility class containing methods for class interrogation.
*/
class GroovyFormsClassUtil {
/**
* Interrogates the class for all declared fields and
* stores the type and name in a container class
* @param the {@link Class#getCanonicalName() canonical name of the class}
* @see Class#getCanonicalName()
* @return a reference to a container class containing the type
*/
static def getModel(fields) {
def domainModel = new GroovyFormsDomainModel()
def names = domainModel.fieldNames.&add;
def types = domainModel.fieldTypes.&add;
def f = fields.each {Field field ->
names field.name
types field.type.canonicalName
}
domainModel
}
}
With groovy, it's so easy and concise (as you can see). Let's explain what's going on. First, I pass in the Field array I get from Field.getDeclaredFields(). Now, groovy adds methods onto the standard JDK classes, one of those methods is a method named each() which takes a closure. Now, back to the point, I pass a Field into the closure. That closure is executed for each element (in this case, a Field). Then I add the name, and the type to a List stored in a container class, now that container class is also written in groovy!
package org.openmrs.module.groovyforms.metadata.model
/*
* Ths class holds information about the properties of the model.
*/
class GroovyFormsDomainModel {
/**
* The field names
*/
def fieldNames = []
/**
* The field types
*/
def fieldTypes = []
}
Now, the fields are Lists, not arrays. Anyways, I've gotten off on a tangent here, so let me get back on track.
What I accomplished:
Week 1:
Code to generate the directory structure, serialization of metadata back/forth between XML and POJOs (Plain Old Java Objects), Wrote tests to ensure everything works in that regard. Additionally, I wrote the code to interrogate the domain model which I will generate the forms from.
Next Week
Write up the templates for the view/controller and write code to do the generation of the view/controller. Write some tests to ensure everything generates correctly.
Unrelated to that, my stored-value card from google came today. It feels nice to be $500 richer! This is going to be the best summer, I'm already having fun doing this. It's amazing seeing the whole project evolve into something amazing.
Google Summer of Code 2009 officially ended on August 17, 2009 at 19:00 UTC (12:00 PDT). I had an amazing time this summer. While stressful at times, it was well worth it.
I would like to thank the following people for my success this summer:
My mentor Mike Seaton for helping me throughout various misunderstandings surrounding the project's requirements. I probably made him work more than he had to. I attribute the success this summer to that one factor. This man is quite brilliant and knows his stuff.
My backup mentor Darius Jazayeri, for stepping in and helping at times and providing guidance when necessary. He is truly a brilliant man.
Ben Wolfe for answering any questions I had. He didn't have to help, but he did. I am very grateful.
Paul Biondich and Burke Mamlin for believing in me and giving me a second chance this summer. Did I mention these guys are doctors? Burke, an internist and Paul a pediatrician. Sorry for clumping you guys together.
Finally, without Leslie Hawthorn, Cat Allman, and Ellen Ko from Google, this program would not exist. They deserve a huge thank you. These three women are amazing. Thank you so much!!
To download it use the svn version: do a checkout:
svn co http://svn.openmrs.org/openmrs-modules/facilitydataTo create a report:
1) Create all your questions
2) Create all of your form sections
3) Finally add all the sections to your form schema
I'll go into detail how to do each:
Creating/Managing questions
From the admin screen: select "Manage Questions."
Now, once on the question management page you will see a list of existing questions. under the "Action" column is a garbage can image -- this deletes the question from the database and is irreversible.
Once created, a question's data type cannot be changed, this is due to the design.
1) name - required
2) question data type - required
3) aggregation method - required
4) description - optional
Question Data Types are as follows:
NumericQuestion - a question that has a numeric answer (optionally has a min/max value and whether or not to allow decimal values.) -- these fields show only if you select "NumericQuestion."
BooleanCodedQuestion - a coded question with 3 answers: "t","f","not applicable"
StockQuestion - a question that tracks stock of vaccines, supplies, etc. This is a special coded question with the following answers: "not_stocked_out", "stocked_out", "expired", "not_applicable" and the comments field used to track the numbers of days stocked out and reason.
To add more questions after you save a question -- click "Add New Question" link above the form box.
Creating/Managing Form Sections
Once you have created all of your questions -- it's time to organize them into sections. You can easily navigate to the "Manage Form Sections" page from the "Add New Question" page.
Now, once on the section management page -- you'll see a list of all sections saved and the associated schema. Next, you'll want to click "Add New Form Section"
Now, once on the add questions page: you'll enter:
1) Display Name - required
2) Description - optional
3) Form Schema -optional (can be deferred)
Mentioned seperately is questions you're adding to the section (these are referred to as form questions by the system).
1) Name
2) Question Number
3) Question
4) Description
To add a question: click "Add New Question" to remove a question, click the "Remove" button next to the button (see image after the one below).
The section below is a section that was defined programmatically in a mock schema that was designed and used throughout the summer.
As you can see, there is a "Remove" button next to each Form Question in the section.
Creating/Managing Form Schemas
From the Section creation page: click "Manage Form Schemas"
Now, after clicking the "Add New Form Schema" link you will see the page below. The following information is needed:
1) Display Name - required
2) Data Entry Frequency -required
3) Validity Period -optional
4) Description - optional
5) Sections specified in this form schema (and the ordering) -- just drag them into the left-side of the pane.
Now, for the shortcomings:
- Ordering of questions cannot be changed once saved
- Sections cannot be associated with two schemas at once.
Viewing and Entering Data
Will be completed at a later date.
So we're in the home stretch folks! 13 days to go until the "Pencils Down" date! Hope everybody's project is going well. Here's how mine is going:
- Reports can be saved and reloaded (with all relevant data filled out (fields etc -- reports can be viewed in editable or "view only" mode).
- Each component of the report (Questions, Form Questions(allows a question to exist on multiple reports), and Sections can be created in isolation
- A page to view the status of a report (complete, partially complete or incomplete) is done for reports that are to be entered daily in a monthly calendar view; monthly still needs to be completed.
Cheers!
Hey folks!
I'm still alive and not dead. The Groovy Forms Module is not a dead project, and WILL be finished. I plan on getting work started back up on it soonish. School is back in session for me. I'm sorry for the long delay.
That is all.
The program officially ended on August 18th. I'm awaiting the arrival of my t-shirt!!
Now, I didn't quite have time to finish my project prior to that date since my uncle suddenly passed away on August 10th.
The project itself is about 80 percent completed. All that is left is to get the rendering working and finish up the management page to edit the form metadata as it appears in the system.
My time is now limited since school started up again for me, so as much time as I can I'm going to devote to finishing up this project.
I'd like to thank the following:
- Burke Mamlin for helping me and answering all the questions I had and guiding me when I was lost.
- Ben Wolfe for putting up with me and helping me when Burke was MIA.
- OpenMRS and its wonderful worldwide network of developers for making this project into what it has become.
- Paul Biondich for encouraging me (along with Burke) to apply for summer of code.
- Last, but certainly not least, Leslie Hawthorn for managing this program and every problem that popped up. She must have super human powers or something.
The list can only be so long, I feel like I'm accepting an academy award here. It was a great experience, and I will definately continue to maintain my project in whatever free time I can find.
Well folks, I can't believe it's almost over. What a journey. I never imagined I'd be able to do this. I have an (almost) working project. I have a few more things that need to be done.
Let's highlight what IS done:
- GroovyForm and its related metadata is stored
- Model class is interrogated for its properties and those are stored in a container class.
- From the data collected in the container class, I generate markup checking for a predefined set of data types.
- The view and its related controller is generated -- sneaking in some groovy magic of course!
- GroovyForms are successfully saved into the system, along with their respective, model, view and controller.
- Forms are persisted when the module is shutdown and reloaded when it is restarted
- Editing metadata related to the forms currently in the system
- Writing the servlet which handles submissions
- Create some sample forms
- Documentation






