Skip to content
munk edited this page Dec 16, 2016 · 21 revisions

OpenHDS-UI is an Angular 1.5 application that provides a user interface for entering and editing demographic data. This application utilizes the REST services of a Spring 4 application that maintains a consistent record of demographic events (https://github.com/benjamin-heasly/openhds-rest/blob/master/src/main/resources/documentation/the-openhds-overview.md) .

The application is delivered using a simple Express.js application to serve local files. It also takes advantage of Twitter's Bootstrap library for style and many other components.

Dependencies are managed via NPM.

Assumptions

This document assumes you are familiar with Angular 1.x development and the OpenHDS domain.

For an overview of angular concepts, see the Angular tutorial

For an overview of openhds concepts, see the OpenHDS Overview

Architecture Overview

The entry point for the OpenHDS-UI application is app/index.js. This defines the routes and other configuration. In particular, it handles the CORS configuration that allows us to talk to OpenHDS-REST without an intermediate backend controller.

Layout of important modules

  • baseline

    • controller/baseline.js This file controls the logic for the baseline round.
    • view
      • baseline.html Top level baseline view. It includes the other baseline views.
      • baselineInit.html Baseline initialization step - fieldworker and region selection.
      • individualBaseline.html Baseline Individual tab - modal buttons for select/create individual and a list of recently added individuals.
      • locationBaseline.html Baseline Location tab - modal buttons for select/create location and a list of recently added locations.
      • relationshipBaseline.html Baseline Relationship tab - modal buttons for select/create relationship and a list of recently added relationships.
      • socialGroupBaseline.html Baseline Social Group tab - modal buttons for select/create social group and a list of recently added social groups.
  • update

    • controller/update.js This file controls the logic for the update rounds.
    • view
      • update.html Top level update view. It includes the other update views.
      • events.html Event selection view. Allows selection of individuals and displays recently created visits and events.
      • updateInit.html Visit initialization page. Allows selection of location hierarchy, location, visit date, and fieldworker.
  • audit

    • controller/audit.js This file controls the logic for the audit rounds.
    • view
      • audit.html Top level audit view. It includes the other audit views.
      • search.html Search control. It includes other search views.
      • searchByHierarchy.html Search by location hierarchy view.
      • auditDisplayTable.html Table to display search results
      • searchByFieldWorker.html Search by fieldworker view.
  • domain This contains many domain entities. A representative sample is shown below

    • entity/service.js Root HTTP service for making requests to openhds-rest
    • individual
      • service.js Service to get and create individuals. Calls entity/service.js
      • partials
        • individualCreateModal.html Form for Individual creation

components.js

This application reuses a few input components throughout the various forms it uses. In order to simplify the development of views, these were moved to custom directives that handle common patterns.

validation.js

One component, extidinput, may require project specific validation. This file handles that as a custom directive. Other project specific custom validation directives should be included here.

config.json

This is for application specific configuration. At the moment it only contains a single value, openhdsRest, which is the url of the backend server you wish to develop or deploy against.

locales

This module is data for translations. To include a new translation, create a json file called <locale>/translation.json with all of the fields from dev/translation.json.

The configuration for this is an inline script in index.html.

core

This module is the home of several common views. Currently, this consists of the nav bar and some debugging partials. If there are other common parts of the application that do not belong to specific domain entities, they should be placed here.

auth

This module is responsible for authenticating against openhds-rest. Hopefully you will not have to modify this.

baseline

This module contains the controller logic for the baseline visit, as well as the associated views. It has dependencies on the domain module.

update

This module contains the controller logic for the update round, as well as the associated views. It has dependencies on the domain module.

audit

This module contains the controller logic for auditing and updating existing entities, as well as the associated views. It has dependencies on the domain module. This is only visible to users with the Data Manager role.

domain

This module contains domain specific services and the views specific to them. Each service is responsible for making http calls to openhds-rest. Common http operations are under the entity submodule.

Services are responsible for providing request and response object constructors for EntityService. Entity service will use this to correctly serialize a request and response to and from the backend as appropriate.

The following is a comprehensive list of domain types:

  • Entities
    • fieldworker
    • individual
    • location
    • locationHierarchy
    • social group
  • Relations
    • membership: individual -> social group
    • relationship: individual -> individual
    • residency: individual -> location
  • Updates
    • visit: a set of updates at a location
  • Update events
    • death
    • inMigration
    • outMigration
    • pregnancyObservation
    • pregnancyOutcome
    • pregnancyResult

Making a change

The most common change will be updating a view or adding a new service call to openhds-rest.

Suppose for example, you wanted to allow multiple pregnancy results to be associated with a single pregnancy outcome and have a data entry worker be able to add those in a single step.

Updating a view

For this example, you will want to modify pregnancyOutcomeCreateModal.html

The existing markup might look something like:

<selectbox i18next="[html]labels.resultType" id="resultType" ng-model="model.currentPregnancyResult.type">
    <option ng-if="code.codeGroup === 'pregnancyResultType'" ng-repeat="code in model.codes" value = "{{code.codeValue}}">
        {{code.codeName}}
    </option>
</selectbox>

One possible solution would be to add a checkbox with the label more results near this that has some ng-model representation in the controller.

The submit button with this modal calls model.submitPregnancyOutcome, so this will probably need to be modified.

In the controller, rather than making the (current entities)[https://github.com/munk/openhds-ui/blob/master/app/update/controller/update.js#L294] null, you would want to determine if the checkbox has been ticked and update the behavior appropriately.

Updating a service

Suppose with the example above, rather than submitting each Pregnancy Result at the time of it's creation, you wanted to batch the submission, but did not have a bulk endpoint on the backend yet.

You could add a submitBulk method to the PregnancyResult service.

Currently, there is a submit method that looks like this:

service.submit = function(fieldWorker, collectionDate, visit, outcome, child, event) {
    var model = {
        fieldWorker: fieldWorker,
        collectionDate: collectionDate,
        visit: visit,
        pregnancyOutcome: outcome,
        child: child,
        event: event
    };
    return EntityService.submit(urlBase, Request, model);
};

Creating a new submitBulk method in that service might involve adding new code like this:

// note plural events! 
service.submitBulk = function(fieldWorker, collectionDate, visit, outcome, child, events) {
    return events.map(function(event) {
        service.submit(fieldWorker, collectionDate, visit, outcome, child, event);
    });
}

Don't forget to include any new javascript files in app/index.html!

Updating validation rules

One of the most common per project updates will be validating external IDs. This is done on both the frontend and backend.

If you want to require that an ID matches some rule, you will need to update validation.js on the frontend. The function in that module might use some regular expression or other computation to determine if an ID is valid and prevent forms from accepting it.

But we also accept advice from the backend about what external IDs to use, to help prevent duplicates. To create a new rule set, you will need to implement a new class matching the interface described here, and make it a Spring Bean.

This interface provides two methods:

String suggestNextId(Map<String, Object> data)
boolean validateExtId(String extId, Map<String, Object> data)

We first call suggestNextId to populate the appropriate text input and then use validateExtId to verify that any changes made are appropriate.

Once you've created an implementation of this interface, you will need to modify the frontend services that call it to submit the appropriate data blob for you to make a decision about what ID to return or to decide if one is valid. You will also need to update the spring context to prefer this bean as appropriate.

Testing

Unit tests are located under /test. In general, they are driven from the controller layer, but some services have tests as well. They are run with Karma using the Jasmine framework. The coverage threshold is 80%. If test coverage falls below that, the build will fail.

Acceptance tests are located under /e2e-tests. They use protractor and requests.

Specific instructions are included in the README

Reporting Bugs

To report a bug or feature request, add a new issue on the github issue tracker.