Week 12

Coding period has ended last tuesday. In the remaining time I will fix bugs, add tests and write documentation.

Week 12

Time is flying! This Tuesday is already the deadline for the final presentation. Therefore I was working hard to add further essential features for the 1.0 release of the operation-theater-module. Since my last blog post I added support for surgical teams. The automatic scheduler takes care of any conflicts between two surgeries. Furthermore, I added a basic workflow support. It is now possible to create, start and finish a surgery. This feature is also essential when it comes to emergencies. In this case the system has to know wether there is a free operation theater or when the next operation theater becomes available. Scheduling emergiencies is the last big point that I want to accomplish. The rest of the time I will be writing documentation, fix bugs and further clean up the code.

Week 11

The main goal of last week was to increase the efficiency of the scheduling algorithm. Therefore I did a larger refactoring to move to a chained planning variable. I also introduced a nullable planning variable. I had to develop a workaround solution because optaplanner doesn’t yet support nullable chained variables. A nullable planning variable is needed when all operation theaters during the planning period are fully utilized. In this case the remaining surgeries must not be scheduled because there is no timeslot left.

Week 10

Last week I have accomplished several smaller tasks and made refactorings and added unit tests to increase code quality:

  • made call to automatic scheduler asynchronous
  • added constraint that surgeries are only scheduled within available times
  • added dialog to filter operation theaters in the daily calendar view
  • added weekly calendar view
  • added calendar color location attribute
  • automatic scheduler: added difficulty comparator to increase efficiency

Week 9 - DBSetup

In the following blog post I want to introduce you to DBSetup. It is an alternative to DBUnit that is OpenMRS’ standard database tool for unit tests.

DBUnit

For those of you who don’t know DBUnit I will give you a short example: DBUnit uses a flat xml file, where all data is defined

<?xml version='1.0' encoding='UTF-8'?>
<dataset>
    <location_tag location_tag_id="1" name="Operation Theater" description="" creator="1" date_created="2005-01-01 00:00:00.0" retired="false" uuid="af3e9ed5-2de2-4a10-9956-9cb2ad5f84f2"/>

    <location location_id="1" name="OT 1" creator="1" date_created="2008-08-15 13:46:50.0" retired="false" uuid="9356400c-a5a2-4532-8f2b-2361b3446eb1"/>
    <location location_id="2" name="OT 2" creator="1" date_created="2008-08-15 13:46:50.0" retired="false" uuid="9356400c-a5a2-4532-8f2b-2361b3446eb2"/>

    <location_tag_map location_tag_id="1" location_id="1"/>
    <location_tag_map location_tag_id="1" location_id="2"/>
</dataset>

This file creates two new locations and assigns the location tag “Operation Theater” to them. Perhaps you have mentioned fields like “creator”, “date_created”, “uuid” and “retired” that have to be defined. We come back to that later.

These are the disadvantages of DBUnit

  • DBUnit forces you to specify every column that is not nullable and doesn’t have a default value defined in the db schema
  • A best practice is to use one file for one test. This means if a column is added/removed one has to edit all dbunit files!
  • XML is verbose
  • Data definition and test case are in seperate files.

DBSetup

Due to this shortcomings I was curious if there is an alternative approach out there. I launched my search engine and stackoverflow and found other people with the same desire. Shortly afterwards, I came across DBSetup from ninja-squad. It looked very promising and I decided to give it a shot.

The main difference to DBUnit is that there is no xml file. Data that should be inserted is defined with Java code

Here is a short example from their website:

Insert.into("PERSON")
      .withDefaultValue("VERSION", 0)
      .columns("ID", "FIRST_NAME", "LAST_NAME", "BIRTH_DATE")
      .values(1, "John", "Doe", "1975-05-06")
      .values(2, "Mark", "Smith", "1980-07-03")
      .values(3, "Claire", "Connell", "1981-09-17")
      .build();

You see that you don’t have to repeat the column name for each row that you want to insert - that’s nice, but not the killer feature right? But if we look more closely at this example we notice line two:

withDefaultValue("VERSION", 0)

This means we can omit the version field from our inserts. Its older brother

withGeneratedValue(fieldName, valueGenerator)

is even more powerful. With his help we can define sequences of integers, strings and dates out of the box. This is the feature that I love most in DBSetup! In the example above we could even remove the “ID” field by declaring

withGeneratedValue("ID", ValueGenerators.sequence())

This would lead to the same result as the default sequence starts at 1 and is incremented by 1.

"creator", "date_created", ...

Now it’s time to come back to the fields (“creator”, “date_created”, “uuid” and “retired”). During testing, usually I just don’t care about this values. With the help of value generators and a utility function, inserting two locations has become a lot easier:

DbUtil.insertInto(Config.LOCATION).columns("name").values("OT 1").values("OT 2").build()

All other values including the primary key and uuid (“Location1”, “Location2”) are inserted automatically.

The magic happens inside the utility class DbUtil and its inner class Config. You can find the internals in this commit

OpenMRS integration

Finally I want to give you an example of how it looks like inside an actual OpenMRS test class

public class Test extends BaseModuleContextSensitiveTest {

	private static DbSetupTracker dbSetupTracker = new DbSetupTracker();

	@Before
	public void setUp() throws Exception {
		Operation operation = sequenceOf(
				DbUtilDefaultInserts.get(),
				insertInto(Config.PROCEDURE)
						.columns("name", "intervention_duration", "ot_preparation_duration", "inpatient_stay")
						.values("Appendectomy", 35, 25, 4)
						.build(),
				...
		);
		DbSetup dbSetup = DbUtil.buildDBSetup(operation, getConnection(), useInMemoryDatabase());
		dbSetupTracker.launchIfNecessary(dbSetup);
	}
...

Summary

In this blog post I have presented an alternative libary to DbUnit and how it can be integrated into OpenMRS. Value generators are a great way to automatically populate row columns. Data is defined with java code rather than as xml file which is more powerful. If you add a new field to a table you don’t have to rewrite all your tests. The only thing you have to do is to add another default value generator to the config inside DbUtil class.

Your feedback is highly appreciated!