Skip to content

Instantly share code, notes, and snippets.

@csytan
Last active January 3, 2016 14:49
Show Gist options
  • Save csytan/8478731 to your computer and use it in GitHub Desktop.
Save csytan/8478731 to your computer and use it in GitHub Desktop.
FIMS proposal. Feedback welcome.

With the possibility of a new NIMS project, FIMS (Future Information Management System), we have the opportunity to create something better by learning from the issues that have affected us in the past with NIMS.

Overview

Objective: To create an online facility inventory for Ethiopia

Goals:

  • Software Maintainability: "do one thing and do it well"
  • Offline & mobile data collection
  • Updated data should show up immediately on the site
  • Efficient indicator calculations
  • Enable easier data analysis (than flat file based NIMS)

Architecture Diagram

         +----+        +-------+
         |    |        |       |
         |    |        |       |
         |    |        |       |
         | xx |        +---++--+
         | xx |           xxx
         +----+        xxxxxxxxx
                          
       iPhone/Android    Computer

               ^           ^
               |           |
               +--+--------+
                  |
                  |
                  v

            +------------------+                                          +------------------+
            |       FIMS       |             +-------------+              | Backend Service  |
            |------------------|             |  Database   |              |------------------|
            |                  |             |-------------|              |                  |
            |  Web App &       |             |             |              | Updates computed |
            |                  |  <------->  +-------------+   <------>   |                  |
            |  JSON API        |             |             |              | indicators       |
            |                  |             |-------------|              |                  |
            |                  |             |             |              |                  |
            +------------------+             +-------------+              +------------------+

Subdivisions of Ethiopia

Since 1995, Ethiopia has been divided in three levels. From highest to lowest level they are:

  • Regions: 11 regions + 2 chartered cities
  • Zones: more than 68 zones in the country. # of zones per region varies widely.
  • Districts (aka Woreda): 670 rural woreda + about 100 urban woreda

Collecting Facility Data

Facility data will be entered and updated through the website. Offline data collection on iOS/Android devices will be supported through the use of HTML5 APIs.

Assuming we have a phone running Android 3.0 or higher Released 2011, the following HTML5 features should be present:

  • Offline ability
  • Local storage
  • GPS capture
  • Taking facility photos

Offline Updating: Preventing duplicates

Previously we've had problems with duplicate facility data collected at different times. This was tricky since we relied on many different ways to generate facility IDs.

Here is one possible solution:

  • Automatically generate an integer ID when adding a new facility.
  • Downloadable facility data for a certain region to allow offline data updating.
  • Have the ability to merge facilities which have similar GPS coordinates and names.

Computed Indicators

Indicators are attributes of a Facility, District, Zone, or Region. Indicators have an id such as num_children, as well as a descriptive name such as Number of Children.

There are two types of indicators. Normal indicators, which are generated through user input, and Computed indicators which are generated through a calculation.

In order to discourage overly complex calculations as in the past, the following limitations will be put in place:

  1. Facility level

    • Facilities have normal and computed indicators.
    • Computed indicators can only be generated from normal indicators from the same facility.
  2. District level

    • Districts have normal and computed indicators.
    • Computed indicators can make use of normal indicators from the District level.
    • Computed indicators can also make use of any indicator from the Facilities it contains.
  3. Zone level

    • Zones have normal and computed indicators.
    • Computed indicators can make use of normal indicators from the Zone level.
    • Computed indicators can also make use any indicator from the Districts it contains.
  4. Regional level

    • Has normal and computed indicators.
    • Computed indicators can make use of normal indicators from the same level.
    • Computed indicators can also make use of any indicator from the level below.
  5. National level

    • Same as the Regional level (you get the picture)...

Note: The intention is to make dependencies more obvious for the majority of computed indicators. Although these limitations could be easily be sidestepped, the programmer or data scientist will be more aware of when a particular calculation is computationally expensive.

Calculating Computed Integers

By using a decorator to register each computed indicator, we can keep track of its dependencies, as well as its metadata.

class Facility(Model):
    district = RelationField('District')
    num_boys = IntegerField(name='Number of Boys')
    num_girls = IntegerField(name='Number of Girls')
    
    @indicator(name='Number of Children')
    def num_children(self):
        return self.num_boys + self.num_girls

class District(Model):
    zone = RelationField('Zone')
    
    @indicator(name='Total number of children in the district', depends=['Facility.num_children'])
    def num_children(self, facilities):
        total = 0
        for facility in facilities:
            total += facility.num_children
        return total
        
class Zone(Model):
    region = RelationField('Region')
    
    @indicator(name='Total number of children in a zone', depends=['District.num_children'])
    def num_children(self, districts):
        total = 0
        for district in districts:
            total += district.num_children
        return total

...

Here is a simplified example of the updates would be be done after updating the normal indicator num_boys in a facility:

  1. Facility.num_children: 1 computation, 1 database query (1 Facility)
  2. District.num_children: 1 computation, 1 database query (~1000 Facilities in a District)
  3. Zone.num_children: 1 computation, 1 database query (~100 Districts in a Zone)
  4. Region.num_children: 1 computation, 1 database query (~25 Zones in a Region)
  5. Country.num_children: 1 computation, 1 database query (13 Regions in Ethiopia)

In this example, the 5 updates required after updating num_boys would be pushed to a task queue which would later be executed by a background task. In this way, users will not have to wait for updates to occur.

Example 2: Student teacher ratio

class Facility(Model):
    district = RelationField('District')
    num_teachers = IntegerField(name='Number of Teachers')
    num_students = IntegerField(name='Number of Students')

    @indicator
    def student_teacher_ratio(self):
        if num_students and num_teachers:
            return num_students / num_teachers


class District(Model):
    zone = RelationField('Zone')

    @indicator(depends=['Facility.num_teachers'])
    def num_teachers(self, facilities):
        return sum_attr(facilities, 'num_teachers')

    @indicator(depends=['Facility.num_teachers', 'Facility.num_students'])
    def student_teacher_ratio(self, facilities):
        facilities = drop_missing(facilities, 'num_teachers', 'num_students')
        self._num_students_for_ratio = sum_attr(facilities, 'num_teachers')
        self._num_teachers_for_ratio = sum_attr(facilities, 'num_students')
        return self._num_students_for_ratio / self._num_teachers_for_ratio


class Zone(Model):
    region = RelationField('Region')

    @indicator(depends=['District.student_teacher_ratio'])
    def student_teacher_ratio(self, districts):
        return sum_attr(districts, '_num_students_for_ratio') / sum_attr(districts, '_num_teachers_for_ratio')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment