InfoPath Complex features for concepts, forms, fields and formentry

So you've got some idea of how to do standard things in OpenMRS form design like questions and answers but have realised some data collection is more complex than that. This page is to show examples of some more complex features and should lead you step by step through creating the concepts, designing the schema and designing the form in MS Infopath, the most common form designing tool.

Sets and repeating tables

It is not uncommon to want to collect data in a table format. For example, you may want a table of medications planned for the patient, and for each medication you want to record the dose, the form (tablet, syrup etc) and the number of units of the dose dispensed. Here is how you do it:

Concept design

The key here is having one concept which links a whole row of the table together in a set. Lets call it "Current Medication Order". This is the concept for the whole row of the table. When setting up this concept in the concept dictionary, tick the "Is set" tickbox. The class is ConvSet. The datatype you can just leave as N/A.

Now set up concepts for each column in your table. In our example they will be called "Medication orders" (coded datatype), "Medication dose" (text datatype), "Medication form" (coded datatype linked to tablet, syrup etc as answer concepts) and "Number of units of medication dispensed" (numeric datatype).

Now go back to your set concept representing the row ("Current Medication Order") and add the four column concepts as members of the set.

Form schema design

In your form schema, drag over your set concept ("Current Medication Order") onto the schema. In the "Max" box, enter -1. This represents infinite and means that table can have infinite number of rows. Notice that as it is a set, it has a little + sign next to it, and if you click it, you see the four members of the set underneath without you having to drag them over separately.

Note that you should NOT tick "select multi" for the table row concept or for the column concepts. This may seem counter-intuitive as the user is, after all, choosing multiple medications. However in each row, the user is only choosing one medication and that is why you should leave the select-multi box unticked.

Infopath design

In infopath, drag the whole set concept ("Current Medication Order") onto the form. Some options will pop up and you can choose "Repeating table". This will give you a quite unwieldy and confusing looking table. The first two columns called "OpenMRS concept" and "OpenMRS datatype" you can just delete - they are not needed. All the other columns will have five or six boxes in them. When you click on the boxes you will notice they are linked to "OpenMRS concept", "OpenMRS datatype", "multiple", "date", "time" and "value" under each of the four column concepts in the data source view in the task pane on the right. You are only interested in the bottom box in each column linked to "value" so delete all the other boxes. You are left with one box in each of the four columns. The coded datatype columns will show up as drop down lists, which is fine in this case. You may like to format the drop down lists by right clicking and choosing "drop down list properties" and under list box entries, click on each one in turn, modify, and change the display name to remove the HL7 bits which do not look pretty. (Change the display name only not the value!)

The result

That's it. In preview mode of the form, you will now see that there is a repeating table - one row displays and the user can add more rows by choosing "insert item". In each row the user can enter data in the four columns.

You may be interested in how this is stored in the database. In the obs table there is a field called obs_group_id. This links all the members of a set of observations together. For example, medication, dose, form and units are all separate observations. If the user filled in three rows in the table there would be 12 observations. The observations from the same row are linked together by having the same obs_group_id. So in this case we will have three different obs_group_id numbers and each number is linked to four observations. So there will never be a mix up of thinking the dose entered in the second row of the table applied to the medication in the first row of the table - we will know which is which from the obs_group_id.

The taskpane widget

In the example above we were a bit vague about how the user chooses the medication itself. Of course you could have put all possible answers in the "medication orders" concept and then the user would have a drop down list of medications. However, this is a bit tedious from the implementer's point of view as you have to spend a lot of time linking all possible medication answers to the "medication orders" question and then in infopath you have to format each of these in the drop down list to take out the funny HL7 bits. Even from a user's point of view it is not always so nice as they have to scroll through a very long drop down list.

An alternative to drop down boxes with hundreds of entries in the above example, and also an alternative to hundreds of option buttons or tick boxes in other examples, is the taskpane widget. The taskpane widget is a way for the user to search for the concept he or she wants, either from the whole concept dictionary or from a particular class of concepts in the concept dictionary.

To the user, the taskpane widget works as follows: they click a button, over on the right hand taskpane a search box appears, they enter a few letters and see the search results, they click on the correct concept and it appears in the box on the form.

Concept design

The main thing you have to keep in mind during concept design when planning to use the taskpane widget is to put concepts in appropriate classes. In our example, you should make sure all medication drug concepts are classified as "drug" (which hopefully you did as you were going along rather than having to go back and review them all now).

Infopath design

The developers have included some nice javascript code in the "basic form" included with OpenMRS so if your form was developed using a duplicate of this basic form (as is normally done), the code will be there in the background of your form (in a file called openmrs-infopath.js in the extracted form, in case you are interested). This code will run a little part of the OpenMRS web application in the right hand taskpane for the user. So your job as an implementer is to create a button and make the button call this code.

In design view of your infopath form, choose design tasks on the right hand side, choose controls and drag a button onto your form. (In the example above, the button should go just under your repeating table but not in it.) Right mouse click the button and choose button properties. Change the label to something nice for the user like "Choose medication". Change the ID to something you the implementer will recognise another day like "btnChooseMedication". Then click "Edit Form Code". You are now in the script editing window.

Infopath will put this in for you:

function btnChooseMedication::OnClick(eventObj)
{
	// Write your code here
}

You must replace "// Write your code here" with a call to the function taskPaneNavigateTo written by the OpenMRS developers. This function takes a string argument which is the query string to be passed to the OpenMRS web application. The query string is in the format file?variable=value&variable=value&variable=value which web programmers are familiar with. This query string cannot have any spaces in it so you have to replace spaces with %20. The file in the web app which provides the concept search is called concept.htm.

The variables in our example are as follows:

classname=drug (this decides the class of concepts which the user will search)

includeDrugConcepts=true

title=Choose%20medication (this appears above the search box in the task pane. %20 represents a space)

createConceptList=true

nodepath=//obs/current_medication_order/medication_orders (this shows which part of the data source of the form the medication concept will go into when the user chooses it)

So in other words you can put in the following code inside the curly brackets:

taskPaneNavigateTo("/concept.htm?
className=Drug&includeDrugConcepts=true&title=Choose%20medication&createConceptList=true
&nodePath=//obs/current_medication_order/medication_orders");

(Remember to change all "&" strings to just "&" when copying/pasting into infopath)

After copying this code to InfoPath, you will need to REMOVE all the 'amp;' after '&' from your codes.

A semi colon goes at the end of the line in javascript.

For more details and options for this code you can see FormEntry Module Taskpane Widgets

You can customise this for other uses, for example if you have different concepts in your schema where the chosen concept is to be stored you must change the node path, if you want a different title then change the title, if you want to search within a different class of concepts then change the classname.

Now you may notice that if the node path leads to a text box on your form, the text the user will see in the box after clicking their chosen concept in the taskpane will be the full HL7 reference. So after choosing aspirin in the task pane, the user will see 88^ASPIRIN^99DCT in the medication_orders box. To make it display nicely, you can change the text box to an expression box (right mouse click, change to, expression box). Then right click the expression box and choose expression box properties. In xpath, type
substring-before(substring-after(., ""), "")
This will make the concept display nicely in the box, and also will prevent the user typing in the box directly, which of course you don't want them to do.

If the taskpane widget is being used to search for a concept which is going into a table, as in our example, you can also put some extra variables at the end of your query string which will ask the user to fill in the other columns of the row of the table for that concept in the taskpane instead of in the form itself.

taskPaneNavigateTo("/concept.htm?
className=Drug&includeDrugConcepts=true&title=Choose%20medication&createConceptList=true
&nodePath=//obs/current_medication_order/medication_orders&extraNodePath=//obs/current_medication_order/medication_dose
&extraLabel=Medication%20Dose&extraNodePath=//obs/current_medication_order/number_of_units_of_medication_dispensed
&extraLabel=Units%20of%20Medication%20Dispensed");

(Remember to change all "&" strings to just "&" when copying/pasting into infopath)

or

firstExtraNodePath=//obs/current_medication_order/medication_dose
firstExtraLabel=Medication%20Dose
secondExtraNodePath=//obs/current_medication_order/number_of_units_of_medication_dispensed
secondExtraLabel=Units%20of%20Medication%20Dispensed

var url = "/concept.htm?
className=Drug&includeDrugConcepts=true&title=Choose%20medication&createConceptList=true
&nodePath=//obs/current_medication_order/medication_orders"

url = url + "&extraNodePath=" + firstExtraNodePath;
url = url + "&extraLabel=" + firstExtraLabel;

url = url + "&extraNodePath=" + secondExtraNodePath;
url = url + "&extraLabel=" + secondExtraLabel;

taskPaneNavigateTo(url);

(Remember to change all "&" strings to just "&" when copying/pasting into infopath)

The result

In our example:

If you did not use the extra variables:
The user clicks a button "Choose medication". In the task pane on the right they get a search box. They type a few letters aspi. Some medications appear meeting this search. They click on aspirin. In the table, they see aspirin in the next blank row. In the aspirin row they fill in the other columns for that row in the table - dose, units dispensed, form.

If you used the extra variables:
The user clicks a button "Choose medication". In the task pane on the right they get a search box. They type a few letters aspi. Some medications appear meeting this search. They click on aspirin. In the task pane they are prompted for medication dose and units of medication dispensed. They enter these and click submit. In the table, they see aspirin, the dose and the units dispensed all filled in in one row of the table.