dimagi/commcare-hq — pull request #5168 by kaapstorm (2014-12-29 12:34:14+00:00)
Title: DHIS2 integration for WV Sri Lanka Nutrition project

This is a feature to add DHIS2 integration for the World Vision Sri Lanka Nutrition project. There's quite a bit of code in this PR, so let me add some pointers in here on how to find your way around.

But first, a request. I've been struggling with mocking my tests. I'm sure I'm missing something, but I'm not sure what it is. There are a couple of tests where I need to mock an object, and it's not working the way I expect. You'll see in tests.py, the test Dhis2OrgUnitTest.test_save with a block commented out, and later TaskTest.test_sync_org_units_adds skipped with the message "Fix mocks". The problems are similar. In the second test, you'll see I'm trying to mock an object, Dhis2OrgUnit, but when I try to use it, it's the real one I'm using, not the mocked one. I don't understand why it's happening, and if anyone can tell me, I'd be very grateful.

I also need some advice on how best to tie this code into the rest of CommCare, in terms of configuring it, and scheduling tasks. But more about those as we go.

OK, how to find your way around. The entry point for this feature is tasks.py. On a regular basis (probably daily) a few tasks will run that syncs data between CommCare and DHIS2, and adds data from cases in CommCare as Events in DHIS2.

For now the "periodic_task" decorator is commented out. Please let me know the best way to configure when these tasks should run. (localsettings.py?) The tasks use some functions that I've put into tasks.py. If these ought to move out to a different module, please let me know, but it seemed OK to leave them there.

The three tasks are "sync_org_units", "sync_child_entities" and "send_nutrition_data". Org units are how DHIS2 stores the names of geographical areas, and the facilities inside them. We pull these so that we can associate FLWs and cases with them. DHIS2 Child entities correspond to CCHQ child cases. We pull theirs and push ours. And DHIS2 needs to know the nutrition data that we collect in CCHQ, so we push that.

These tasks use classes defined in models.py. DHIS2 org units are modeled using the Dhis2OrgUnit class. They are persisted as FixtureDataItems. I've used the Django manager pattern for managing result sets, as in "FooClass.objects.all()", but I realised that Dimagi hasn't used that pattern for fixtures elsewhere, and it might be best to change that to be consistent, and move the "all" and "get" methods into the Dhis2OrgUnit class, and delete the FixtureManager class.

The DHIS2 API is managed using the Dhis2Api class. It's got a bunch of methods for doing DHIS2-specific stuff. And it uses JsonApiRequest class to send requests and parse responses. (JsonApiRequest just wraps Kenneth Reitz's requests library, passing authentication and JSON headers as appropriate.)

models.py begins by defining two constants that should probably move somewhere else. I wasn't sure where though, and I'd be grateful if someone could let me know the best place to put them.

The README file includes the spec, but hopefuly what I've written here will give you enough to go on. Of course, just let me know if there's anything else you'd like to know. The README also includes a useful summary of the DHIS2 API documentation, and the assumptions made in the code about the way CommCare app is set up.

P.S. I've just noticed I haven't written a test for the "empty" filter I created. I'll add that to my TODO list of tests.