Angular Form Entry And Schema Version Management

###Background This started as an effort to have an easier way to create and consume forms in angular. We adopted an approach that leverages the features provided by angular formly which is an angular module that provide an easy way to create and use forms in angular applications. We created what we call an OpenMRS JSON schema (here is an example) to facilitate an easy way of capturing OpenMRS specific information. The schema is translated into the format that can be consumed by the angular formly module. It worked pretty well for a while.

As we dived into creating more forms we realized the benefits of creating a separate form management module. We refactored our code and the form management part was taken out in its own new module which conveniently is named openmrs-angularFormentry. It can now be utilized independently.

During all this time we were also thinking of creating reusable form components. This was inevitable for us because most of our forms are answering the same questions with minor differences that depend on things like age and the associated program/project; For example you can have two versions of the same form for childrens & adults. The idea is to provide an ability for someone to define a form and reuse some of the already existing components if they wish to instead of having to redefine the same thing over and over. Also since for all this time our forms have been part of the code we need to take them out and have them stored in OpenMRS for ease of management. That way forms & code can evolve independently.

###Challenges & Proposed Solutions

  • The components will have to evolve with time inevitably but since the old components may have already been used to collect data, there arise a need of versioning and being able to tell which form uses what version. One simple approach is storing components as forms themselves and once any form is published, it is essentially frozen. The components will have versions as well as forms and once you identify a form version you can be sure to know exactly what components versions are associated with it. In essence publishing a form means making sure all associated components are published and any edit to an existing published component/form means creation of a new version. Remembering the form that was used to collect data is enough to load the correct form needed for editing.

  • Tracking the number of forms a particular component has been used in. This is a bit difficult with the existing OpenMRS form model. Forms and components are json schemas stored as form resources and the used components are directly referenced in those schema so figuring out how many forms uses a particular component means scanning these schemas trying to find those references which can be done but without a place to store this information without creating new tables. Anyhow this is low priority currently.

Discussion

The main objective of this thread is to get ideas on how to best approach this endeavor bearing in my mind that we want to get to the point where an end user can easily create/compose, store, retrieve and consume forms using the json schemas ( and eventually UI form builders) without requiring programming skills.

It would be nice also to point out things we might have missed.

4 Likes

Can you give some illustrative example of the sort of things you’d think of as components? Are they “complex widgets” or “form partials” or both?

I think the way we are thinking about this may be very affected by how forms are designed and handled at AMPATH. If you still remember the discussion we had during the OpenMRS Dev Summit in Singapore, we mentioned that our forms are organized as follows:

|__Form
   |__Page-1
   |   |__Section-1
   |   |    |__Question
   |   |    |__Question
   |   |
   |   |__Section-2
   |       |__Question
   |
   |__Page-2
       |__Another-Section

@nkimaina correct me if I am wrong, the way we have implemented components currently makes it possible to specify either an entire form, a page within a form or a section as a component in another form. So basically a component in this case is a unit of data collection.

I hope this answers the question.

Versioning

I agree publishing for versioning (much like tagging of versions in GtiHub) is a reasonable approach to ensure changes to components don’t corrupt other published forms (i.e., only published components can be referenced from a published version of a form).

I would favor adjusting our form model to better represent sequently published versions of a form as a single evolving form (rather than a series of unconnected form records with their relationship inferred from the name field). In our current model, a form record isn’t actually a form; rather, it’s a version of a form.

Abstract the Model

I tried to get this into HTML Form Entry, but missed the boat, and HTML Form Entry is hardcoded only for encounters. Even if we are only tackling encounter forms for now, I would strongly favor building Angular Form Entry with a generic key/pair model that is extended by an EncounterModel. All the encounter-specific business (e.g., requiring a patient, location, etc.) should be in the EncounterModel and not assumed of all forms.

Abstract the Post-Form Handling

There should be a clear point at which the payload (form’s model) is being submitted and this should be performed through an interface (e.g., FormProcessor), so the post-form handling can be customized. Along with the EncounterModel, encounter-based forms could default to an EncounterFormProcessor that creates an encounter via the API; however, I should be able configure a form to call a different processor that doesn’t have to create an encounter. Specifying the processor as a property of a form makes forms much more extensible.

Allow for Archiving

Ideally, form models (the base model, EncounterModel, and any other form models) should support some type of serialization (and deserialization). This will facilitate saving of drafts and the ability to archive forms as they are being sent for processing. One of the benefits of our HL7-based approach to processing of the original InfoPath-based Form Entry forms was having an archive of every form submission. Having these archivable artifacts can both help with data backup/recovery and make processing more flexible (e.g., ability to separate form submission from processing).

Thanks @willa for posting!

@darius, https://github.com/AMPATH/ng-amrs/tree/master/app/scripts/formentry/formschema seems to list a good number of components, which have already been defined.

@willa, we’ll be putting together a quick proof of concept for consuming json forms to display and process forms in OpenMRS Android Client as part of GSoC 2016 with @avijitghosh82. Thanks for opening up the path for us!

I think the approach you outlined for versioning components and forms makes a lot of sense.

I do not see the current model as something tightly coupled with encounters. The only coupling for me is the “processor” field, which says “EncounterFormProcessor”. You could have used a different naming e.g. “model”: “encounter” and be in-line with what I think @burke is after, unless I am misunderstanding.

I am not sure I understand this part @raff

I mean that it may be good to rename “processor” to “model” and “EncounterFormProcessor” to “encouter” so that it is up to the consumer of the form to pick the exact implementation for a form modelled around “encounter”. In case of your angular formly module it will be EncounterFormProcessor, but for Android we can have EncounterFormOfflineProcessor and EncounterFormRestProcessor processors. “Model” carries a bit different meaning for the field, but it is not strictly necessary. I’m just trying to understand what Burke suggested in Abstract the Post-Form Handling and how to apply it.

I’ve opened a thread for the implementation we are going to be doing on Android: Dynamic Form UI Generation from JSON Schema

First we will be starting with the Capture Vitals form, and moving on to the dynamic generation of every form. Thanks!

@willa, could you please point us to any documentation for the json form (what properties are allowed and their meaning, which properties are required)? Here is what I found so far. Do you have more? Do you have any programmatic validator to check if the given json is valid?

Also do you have any convention on storing forms on the server? You mentioned you store them as form resources… Is it free text or long free text? Do your require any specific name? Would the following work http://devtest04.openmrs.org:8080/openmrs/ws/rest/v1/form/7ef3498c-609d-4d92-8b4c-cec1014023c2?v=custom:(uuid,name,resources) ?