Form Designer for Bahmni

FYI – @willa shared an update on AMPATH’s JSON Forms on the 2016-08-11 Developers Forum. I caught most of it (missed a little on the front end) in a webcast that is now posted online:

Cheers,

-Burke :burke:

Development Status Update

Currently form-builder contains ability to create new empty forms which will be listed in consultation page of Bahmni if configured. Also, if the forms controls JSON is created (currently a manual step but will be automated in future releases), the form controls will also be visible on the consultation page. The controls that are currently supported are: Label, ObsControl (text and numeric obs concepts) and section which is a visual grouping.

Glad to see this update Akhil. I know the team has been working hard on this!

Any chance we can see a fragment of a screenshot, or a snippet of json config to see what defining a form might look like?

@darius We should be able to share it by end of this week.

1 Like

This video is a quick update on our progress on Form Builder. Hope it is helpful.

3 Likes

@bharatak this is great progress! I like it. :smile: Are there parts of this module which can be reused in the OpenMRS Reference Application? Something like what you did for the dataintegrity module. :slight_smile:

@bharatak, would it be possible to share a link to the source code? I have some questions, but I can answer them myself seeing the source :slight_smile:

Hi @dkayiwa @raff , Sure. The current effort can be categorized to three things

1. form-controls - ReactJS based NPM module that can be leveraged in any javascript project. Its re-usable and doesn’t depend on Bahmni Distro 2. implementer-interface - This is an independent app and doesn’t depend on Bahmni. It has a canvas using which you can drag-and-drop controls defined in form-controls. Also, you can register custom controls defined in any other npm module. The output of this project is the form definition being stored in form & form_resource table. 3. BahmniApps - This is our Angular App. We render the forms by reading the form & form_resource tables. We wrote a simple glue for rendering react controls in angular app.

@dkayiwa, 1 & 2 can be used by any distro as of now. If there are any custom backend required, we will create an OMOD so that it can be easily plugged to any other distro. Hope this helps.

@bharatak yes this is very helpful! :smile: Can one reuse the bahmni apps for rendering a form in the reference application? Just like the htmlformentry and htmlformentryui modules can be used for rendering htmlforms in any distro?

You need not use bahmniapps for rendering the form. A simple glue can be written in reference app which renders a react component which in this case is a form.

Oh i see! Thanks @bharatak for the feedback! :slight_smile:

Great work team, and thanks @bharatak for sharing the video! (I understand that @angshuonline is going to share some of the design philosophy soon as well.)

To people like @dkayiwa, @raff, and @mogoodrich who have worked on building form entry tools for OpenMRS before, please do give us feedback about how you think we should proceed.

One thought, channeling @burke: we should ensure from the beginning that we don’t hardcode this tool to only work for submitting forms that are encounter transactions. We should support the idea that the form controls are able to build up a model, and this model is handled by code that can be swapped out. (Though of course we’ll focus on the initial or default handler, being the one that mainly creates obs in an encounter transaction.)

@bharatak thanks for posting the video and the update on this project - it looks like great progress.

I particularly like that the form-controls are a separate project that provides widgets to any Javascript project. If I’m understanding right, these could be used by other tools that might need a concept-picker, or a boolean-picker, etc. Is that right? I definitely think it makes sense to separate out the notion of a reusable concept-picker as separate from an obs-control, etc.

In terms of the implementer-interface, I think it would be preferable (from my perspective) for this to create and modify text-based resources (json files) which are stored within the implementation-config and version controlled, rather than have this tool store it’s form definition information directly in the form / form_resource tables in the database (if I’m understanding right). It would make a lot more sense (for us), if these were stored in implementation config and then loaded into the form/form_resource tables either at system start-up, or by an explicit “publish” step.

Interested in your thoughts on this.

Thanks for all of the work on this so far! Mike

2 Likes

@mseaton Thanks for the response.

You are correct with the assumption form-controls is a separate widget library (npm module) that can be leveraged in any javascript project and that each of the UI elements are rendered based on underlying concept’s datatype. We are calling it ObsControl because, the underlying value is picked up and an Obs is created. Having said that the ObsControl (or a WrapperControl - to be made generic very soon) will take a data-source and a data-source-mapper with standard interfaces. So, we can very well construct any kind of object as an output from this control.

TheForm Builder will have an Import/Export feature. The Import option will download the JSON and relevant default locale files that can be version controlled. Storing these in the database helps us do CRUD operations, version it and have an audit of when people change the files. The Form Builder will have a workflow for the forms like DRAFT & PUBLISHED status. This is simpler with API.

This is true from the perspective of designing forms for one implementation on its production server, but @mseaton’s suggestion of text-based resources (audited and version-controlled via git) makes more sense from the perspective of developing a form for use in multiple implementations, or designing it on a dev machine to later deploy to prod.

I would strongly suggest that we support deploying forms by distributing their artifacts as part of the implementation config. (This could be done via Import, or another way.)

@angshuonline / @bharatak - based on this comment (nearly 2 years ago), the implementer-interface was intended to be an independent app that doesn’t depend on Bahmni but could be plugged into any other distro. Is that still the case?

Thanks, Mike

Yes. The intent is still the same. I am wondering if we respected everything though. (e.g API usages) You will need this module and “WebServices REST” module of course. I wonder if we used any of the EMR API http calls. (We use it for submission, but I wonder if we used some other APIs like concept search APIs)

Thanks @angshuonline! I can test out a some point… can you point in the direction of where I’d figure out how to fire up the form builder?

Take care, Mark

@angshuonline… so I checked out the code in implementer-interface and followed the instructions in the README but ran into some issues…

The project didn’t installl properly (ie running “yarn”) at first because it couldn’t find a specific dependency as one of the links in the yarn.lock file appeared to be timing out. So, I deleted the lock file to see if I could get it to find the dependencies on it’s own and it appeared to install and build successfully after that.

When I did a “yarn start” and went to localhost:8080 the menu page with the form builder button came up, but clicking on the button did nothing. I’ll share the errors I received below.

Note that when I first tried I didn’t have a version of Bahmni running, but then I went ahead and fired up an old Vagrant Bahmni install on 192.168.33.10 and made the required changes to httpd.conf, but still no luck… errors below, any thought?

 warning.js:33 Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of `FormBuilderBreadcrumbs`.
    in FormBuilderBreadcrumbs (created by FormBuilder)
    in div (created by FormBuilder)
    in div (created by FormBuilder)
    in div (created by FormBuilder)
    in div (created by FormBuilder)
    in FormBuilder (created by FormBuilderContainer)
    in div (created by FormBuilderContainer)
    in FormBuilderContainer (created by Connect(FormBuilderContainer))
    in Connect(FormBuilderContainer) (created by RouterContext)
    in div (created by App)
    in App (created by RouterContext)
    in RouterContext (created by Router)
    in Router
    in Provider
printWarning @ warning.js:33
warning @ warning.js:57
createElement @ ReactElementValidator.js:190
FormBuilderBreadcrumbs @ FormBuilderBreadcrumbs.jsx:18
(anonymous) @ ReactCompositeComponent.js:303
measureLifeCyclePerf @ ReactCompositeComponent.js:73
_constructComponentWithoutOwner @ ReactCompositeComponent.js:302
_constructComponent @ ReactCompositeComponent.js:277
mountComponent @ ReactCompositeComponent.js:185
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
performInitialMount @ ReactCompositeComponent.js:368
mountComponent @ ReactCompositeComponent.js:255
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
performInitialMount @ ReactCompositeComponent.js:368
mountComponent @ ReactCompositeComponent.js:255
mountComponent @ ReactReconciler.js:43
performInitialMount @ ReactCompositeComponent.js:368
mountComponent @ ReactCompositeComponent.js:255
mountComponent @ ReactReconciler.js:43
updateChildren @ ReactChildReconciler.js:119
_reconcilerUpdateChildren @ ReactMultiChild.js:204
_updateChildren @ ReactMultiChild.js:308
updateChildren @ ReactMultiChild.js:295
_updateDOMChildren @ ReactDOMComponent.js:944
updateComponent @ ReactDOMComponent.js:758
receiveComponent @ ReactDOMComponent.js:720
receiveComponent @ ReactReconciler.js:122
_updateRenderedComponent @ ReactCompositeComponent.js:751
_performComponentUpdate @ ReactCompositeComponent.js:721
updateComponent @ ReactCompositeComponent.js:642
receiveComponent @ ReactCompositeComponent.js:544
receiveComponent @ ReactReconciler.js:122
_updateRenderedComponent @ ReactCompositeComponent.js:751
_performComponentUpdate @ ReactCompositeComponent.js:721
updateComponent @ ReactCompositeComponent.js:642
receiveComponent @ ReactCompositeComponent.js:544
receiveComponent @ ReactReconciler.js:122
_updateRenderedComponent @ ReactCompositeComponent.js:751
_performComponentUpdate @ ReactCompositeComponent.js:721
updateComponent @ ReactCompositeComponent.js:642
performUpdateIfNecessary @ ReactCompositeComponent.js:558
performUpdateIfNecessary @ ReactReconciler.js:154
runBatchedUpdates @ ReactUpdates.js:148
perform @ Transaction.js:141
perform @ Transaction.js:141
perform @ ReactUpdates.js:87
flushBatchedUpdates @ ReactUpdates.js:170
closeAll @ Transaction.js:207
perform @ Transaction.js:154
batchedUpdates @ ReactDefaultBatchingStrategy.js:60
batchedUpdates @ ReactUpdates.js:95
dispatchEvent @ ReactEventListener.js:145
invariant.js:42 Uncaught Error: Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object. Check the render method of `FormBuilderBreadcrumbs`.
    at invariant (invariant.js:42)
    at ReactCompositeComponentWrapper.instantiateReactComponent [as _instantiateReactComponent] (instantiateReactComponent.js:72)
    at ReactCompositeComponentWrapper.performInitialMount (ReactCompositeComponent.js:364)
    at ReactCompositeComponentWrapper.mountComponent (ReactCompositeComponent.js:255)
    at Object.mountComponent (ReactReconciler.js:43)
    at ReactDOMComponent.mountChildren (ReactMultiChild.js:234)
    at ReactDOMComponent._createInitialChildren (ReactDOMComponent.js:701)
    at ReactDOMComponent.mountComponent (ReactDOMComponent.js:520)
    at Object.mountComponent (ReactReconciler.js:43)
    at ReactDOMComponent.mountChildren (ReactMultiChild.js:234)
invariant @ invariant.js:42
instantiateReactComponent @ instantiateReactComponent.js:72
performInitialMount @ ReactCompositeComponent.js:364
mountComponent @ ReactCompositeComponent.js:255
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
performInitialMount @ ReactCompositeComponent.js:368
mountComponent @ ReactCompositeComponent.js:255
mountComponent @ ReactReconciler.js:43
mountChildren @ ReactMultiChild.js:234
_createInitialChildren @ ReactDOMComponent.js:701
mountComponent @ ReactDOMComponent.js:520
mountComponent @ ReactReconciler.js:43
performInitialMount @ ReactCompositeComponent.js:368
mountComponent @ ReactCompositeComponent.js:255
mountComponent @ ReactReconciler.js:43
performInitialMount @ ReactCompositeComponent.js:368
mountComponent @ ReactCompositeComponent.js:255
mountComponent @ ReactReconciler.js:43
updateChildren @ ReactChildReconciler.js:119
_reconcilerUpdateChildren @ ReactMultiChild.js:204
_updateChildren @ ReactMultiChild.js:308
updateChildren @ ReactMultiChild.js:295
_updateDOMChildren @ ReactDOMComponent.js:944
updateComponent @ ReactDOMComponent.js:758
receiveComponent @ ReactDOMComponent.js:720
receiveComponent @ ReactReconciler.js:122
_updateRenderedComponent @ ReactCompositeComponent.js:751
_performComponentUpdate @ ReactCompositeComponent.js:721
updateComponent @ ReactCompositeComponent.js:642
receiveComponent @ ReactCompositeComponent.js:544
receiveComponent @ ReactReconciler.js:122
_updateRenderedComponent @ ReactCompositeComponent.js:751
_performComponentUpdate @ ReactCompositeComponent.js:721
updateComponent @ ReactCompositeComponent.js:642
receiveComponent @ ReactCompositeComponent.js:544
receiveComponent @ ReactReconciler.js:122
_updateRenderedComponent @ ReactCompositeComponent.js:751
_performComponentUpdate @ ReactCompositeComponent.js:721
updateComponent @ ReactCompositeComponent.js:642
performUpdateIfNecessary @ ReactCompositeComponent.js:558
performUpdateIfNecessary @ ReactReconciler.js:154
runBatchedUpdates @ ReactUpdates.js:148
perform @ Transaction.js:141
perform @ Transaction.js:141
perform @ ReactUpdates.js:87
flushBatchedUpdates @ ReactUpdates.js:170
closeAll @ Transaction.js:207
perform @ Transaction.js:154
batchedUpdates @ ReactDefaultBatchingStrategy.js:60
batchedUpdates @ ReactUpdates.js:95
dispatchEvent @ ReactEventListener.js:145
ReactReconciler.js:60 Uncaught TypeError: Cannot read property 'getHostNode' of null
    at Object.getHostNode (ReactReconciler.js:60)
    at ReactCompositeComponentWrapper.getHostNode (ReactCompositeComponent.js:381)
    at Object.getHostNode (ReactReconciler.js:60)
    at Object.updateChildren (ReactChildReconciler.js:111)
    at ReactDOMComponent._reconcilerUpdateChildren (ReactMultiChild.js:204)
    at ReactDOMComponent._updateChildren (ReactMultiChild.js:308)
    at ReactDOMComponent.updateChildren (ReactMultiChild.js:295)
    at ReactDOMComponent._updateDOMChildren (ReactDOMComponent.js:944)
    at ReactDOMComponent.updateComponent (ReactDOMComponent.js:758)
    at ReactDOMComponent.receiveComponent (ReactDOMComponent.js:720)

Whoops, sorry @angshuonline, I missed the “this module” link you referenced (openmrs-module-bahmni.ie.apps)… I will try installing that… thanks @mseaton for pointing that out.

Take care, Mark