Skip to content

d2‐autogen‐forms

deeonwuli edited this page Feb 28, 2024 · 4 revisions

d2-autogen-forms. Data Entry custom forms on steroids.

General information

d2-autogen-forms is a tool that generates custom forms for datasets. It is developed using d2-reports. The DHIS2 data entry app provides a default view for users to collect information for different datasets. However, users can add any custom form (in any valid HTML, CSS, and JS) and modify the default view. This allows users to tailor the data entry app to their specific needs.

How it works

Different functionalities can be added to each data element in the dataset by configuring certain settings that are saved in the dataStore. The configuration depends on the following elements, which need to be defined:

  • dataSets
  • dataElements
  • categoryCombo

An example:

{
  "categoryCombinations": {
    "CC_CODE1": {
      "viewType": "shortName"
    }
  },

  "dataElements": {
    "DE_CODE1": {
      "selection": {
        "isMultiple": true,
        "optionSet": {
          "code": "OPTION1_CODE"
        }
      }
    }
  },

  "dataSets": {
    "DS1_CODE": {
      "viewType": "table",
      "tabs": {
        "active": true,
        "order": 1.2
      }
    },
    "DS2_CODE": {
      "viewType": "grid",
      "sections": {
        "SECTION1_CODE": {
          "viewType": "table",
          "texts": {
            "header": "<h2>Section header from dataStore</h2>",
            "footer": "<h3>Section footer from dataStore</h3>"
          }
        }
      },
      "texts": {
        "footer": {
          "code": "MAL-ADP-CF-FTR"
        },
        "header": {
          "code": "MAL-ADP-CF-HDR"
        }
      }
    },
    "DS3_CODE": {
      "viewType": "grid",
      "sections": {
        "SECTION2_CODE": {
          "viewType": "grid-with-periods",
          "toggle": {
            "type": "dataElement",
            "code": "CODE_OF_DATA_ELEMENT"
          },
          "periods": {
            "type": "relative-interval",
            "endOffset": 0,
            "startOffset": -2
          }
        }
      }
    }
  }
}

View Types

View mode: Table

Data elements will be shown in the first column and the value in the second.

An example, a section with data elements:

-   `ITNs - Policy`
-   `ITNs - Implemented`

This will create this table:

                   |                |
ITNs - Policy      |                |
ITNs - Implemented |                |

View mode: Grid

Data elements will by grouped by rows and columns using formName/name and " - " as a separator of subsection / data element.

An example, a section with data elements:

-   `ITNs - Basic - Written Policy`
-   `ITNs - Basic - Policy Implemented`
-   `ITNs - Extended - Written Policy`
-   `ITNs - Extended - Policy Implemented`

This will create this table:

                | Written Policy | Policy Implemented |
ITNs - Basic    |                |                    |
ITNs - Extended |                |                    |

NOTE: The section processor detects if all the data elements are indexed, and in that case a special. An example:

Data elements:

- `MAL - Compound name (1)`
- `MAL - Compound name (2)`
- `MAL - Compound symbol (1)`
- `MAL - Compound symbol (2)`

Output:

   #   | Compound name | Compound symbol
   1   |               |
   2   |               |

View mode: Grid with periods

Data elements will by grouped by rows and subrows using formName/name and " - " as a separator of subsections.

An example, a section with data elements:

-   `ITNs - Basic`
-   `ITNs - Extended - Written Policy`
-   `ITNs - Extended - Policy Implemented`
-   `ITNs - Extended - Policy Extra`

And having a configuration in the dataStore:

{
  "SECTION_CODE": {
    "viewType": "grid-with-periods",
    "periods": {
      "type": "relative-interval",
      "endOffset": 0,
      "startOffset": -2
    }
  }
}

And rendering a data entry for 2022, will create this table for interval [2022-2 .. 2022+0]:

                                       |  2020  |  2021  |  2022   |
--------------------------------------------------------------------
ITNs - Basic                           |        |        |         |
                 | Written Policy      |        |        |         |
ITNs - Extended  | Policy Implemented  |        |        |         |
                 | Policy Extra        |        |        |         |

View mode: Grid with totals

View mode: Grid with combos

View mode: Grid with category option combos

This is the default data entry form, where the data elements and category option combinations.

An example for section ITNs:

      Data element                                Category option combos
- ITNs - Basic                            [Public, Private, Community, Combined]
- ITNs - Extended - Written Policy        [Public, Private, Community, Combined]
- ITNs - Extended - Policy Implemented    [Public, Private, Community, Combined]
- ITNs - Extended - Policy Extra          [Public, Private, Community, Combined, default]

This will create this table:

                                       |  Public  |  Private  |  Combined  |  Community  |  default
----------------------------------------------------------------------------------------------------
ITNs - Basic                           |          |           |            |             |
                 | Written Policy      |          |           |            |             |
ITNs - Extended  | Policy Implemented  |          |           |            |             |
                 | Policy Extra        |          |           |            |             |

View mode: Grid with subnational OUs

Installation

Install and build the app by running:

$ yarn install # first time only
$ yarn build

This will create a custom-data-form.html file in the dist folder at the root of the project. Subsequently, you can navigate to the dataset > design data entry form > paste the HTML content in the editor and then see your changes in the data entry.

On development, use:

$ HOST=<address> PORT=<port> REACT_APP_DHIS2_BASE_URL=<dhis2_instance_url> REACT_APP_DHIS2_AUTH=<user:passwd> yarn start

For example:

$ HOST=localhost PORT=8082 REACT_APP_DHIS2_BASE_URL=http://localhost:8080 REACT_APP_DHIS2_AUTH='admin:district' yarn start

To test on your local instance, the orgUnit, dataSet and period query parameters need to be set and the URL should be formatted as follows: http://localhost:8081?orgUnit=ID&dataSet=ID&period=PERIOD

Basic configuration of the metadata

Expected structure of the metadata:

  • Maintenance -> Data set -> Edit: Add all the data elements that need to be displayed.
  • Maintenance -> Data set -> Manage sections: Create a section for each table to display in the form.

Advanced customizations on datastore

columnsDescriptions

Section columns can be further customized to have a description:

{
  "columnsDescriptions": {
    "Combined": "<%= DATA_ELEMENT_COMBINED %> Only data that cannot be disaggregated into separate health sectors",
    "Community": "Only data from the community health workers",
    "Private": "Only data from the private health facilities",
    "Public": {
      "code": "PUBLIC"
    }
  }
}

where Combined, Community, Private, Public are the column names from the table and {code: "PUBLIC"} is referencing a constant code that is used to store the description.

The column descriptions can also be controlled by a data element. As seen above, DATA_ELEMENT_COMBINED is the code of the data element used to change the value of the column description for the Combined column.

optionSet

It is possible to configure an optionSet for data elements so that a drop-down menu will be displayed instead of the classic input component in the autogenerated form. This is a useful option when a user has a TEXT data element but wishes to restrict the values that can be entered.

An illustration of this configured in the datastore would be:

{
  dataElements": {
    "DE_CODE1": {
      "selection": {
        "isMultiple": true,
        "optionSet": {
          "code": "OPTION_CODE1"
        }
      }
    },
  }
}

Here, we are configuring the autogenerated form to display a multiselect dropdown field to the user with the code "DE_CODE1". The values for the drop-down field will be the values in the optionSet with the code "OPTION_CODE1".

rules

We can add rules to a specific data element to modify the state of other dataElements:

{
  "dataElements": {
    "DATA_ELEMENT_CODE": {
      "rules": {
        "disabled": {
          "dataElements": ["DATA_ELEMENT_CODE_1", "DATA_ELEMENT_CODE_2"],
          "condition": "false"
        },
        "visible": {
          "dataElements": ["DATA_ELEMENT_CODE_3"],
          "condition": "true"
        }
      }
    }
  }
}

A rule has two params:

  • data elements to be affected and
  • a condition that will be compared against the value of the current data element.

And two types:

  • disabled
  • visible

Here we are adding a disabled rule and a visible rule for the dataElement with code DATA_ELEMENT_CODE. The rules config above is basically doing this:

  • When the value of the dataElement DATA_ELEMENT_CODE is equal to false, the data elements DATA_ELEMENT_CODE_1 and DATA_ELEMENT_CODE_2 will be disabled.
  • When the value of DATA_ELEMENT_CODE is equal to false, the data element with code DATA_ELEMENT_CODE is hidden.

styles

A section has three distinct containers: title, columns and rows. The styles config gives us the option to add a background color to each container

{
  "styles": {
    "columns": {
      "backgroundColor": "#cd853f"
    },
    "rows": {
      "backgroundColor": "#F7F7F7"
    },
    "title": {
      "backgroundColor": "#FFC0CB"
    },
    "totals": {
      "backgroundColor": "#01bff080"
    }
  }
}

tabs

Sections can be customized as tabs.

{
  "tabs": {
    "active": true,
    "order": 0.1
  }
}

The sections will be arranged as tabs according to the order property which must always be a float.

  • All sections with the same order number before the decimal will be in the same tab
  • The sections in each tab will be arranged according to the order number after the decimal in an ascending order.

texts

Texts in different parts of the section can be customized. They can either be stored as strings or referenced from a constant that stores the string.

{
  "texts": {
    "header": {
      "code": "SECTION_HEADER"
    },
    "footer": "Footer text",
    "name": "Section name text",
    "rowTotals": "Row totals",
    "totals": "Column totals text"
  }
}

titleVariant

This section property customizes the style of the section name:

{
  "titleVariant": "h1" | "h2" | "h3" | "h4" | "h5" | "h6"
}

toggle

Section contents can be optionally shown/hidden using a dataElement as toggle. In the section configuration:

{
  "toggle": {
    "type": "dataElement",
    "code": "DATA_ELEMENT_CODE",
    "condition": "false"
  }
}

This means the section would only be visible when the value of DATA_ELEMENT_CODE is equal to false.

toggleMultiple

Section contents can be optionally shown/hidden using multiple data elements as toggle. In the section configuration:

{
  "toggleMultiple": [
    {
      "dataElement": "DATA_ELEMENT_CODE_1",
      "condition": "true"
    },
    {
      "dataElement": "DATA_ELEMENT_CODE_2",
      "condition": "false"
    }
  ]
}

This means the section would only be visible when the value of DATA_ELEMENT_CODE_1 is equal to true AND DATA_ELEMENT_CODE_2 equal to false

totals

  • For horizontal totals we need to activate the checkbox Show row totals in the edit section modal in Maintenance -> Dataset -> Manage Section. Then in the json config we can include a rowTotals attribute to the texts section to control the name of the column.
{
  "texts": {
    "rowTotals": "Total"
  }
}

or we can use a constant code:

{
  "texts": {
    "rowTotals": {
      "code": "CODE_CONSTANT"
    }
  }
}
  • For vertical columns we have the totals attribute. We need the code of the dataElements we want to interact and a formula where we can specify what kind of calculation we want to do.
{
  "texts": {
    "totals": {
      "dataElementsCodes": ["DATA_ELEMENT_1", "DATA_ELEMENT_2"],
      "formula": "<%= DATA_ELEMENT_1+DATA_ELEMENT_2 %>"
    }
  }
}

The formula above sums the data values in DATA_ELEMENT_1 and DATA_ELEMENT_2.

We might also need to add a different formula for each column, for this we can use the formulas object that allows a reference to each column by its name:

{
  "texts": {
    "totals": {
      "dataElementCodes": [
        "DATA_ELEMENT_COMM",
        "DATA_ELEMENT_COMM_TOTAL",
        "DATA_ELEMENT_PRIV",
        "DATA_ELEMENT_PRIV_TOTAL",
        "DATA_ELEMENT_PUB",
        "DATA_ELEMENT_PUB_TOTAL"
      ],
      "formulas": {
        "Community based": {
          "formula": "<%= Math.round((DATA_ELEMENT_COMM/DATA_ELEMENT_COMM_TOTAL)*100) %>"
        },
        "Private Sector": {
          "formula": "<%= Math.round((DATA_ELEMENT_PRIV/DATA_ELEMENT_PRIV_TOTAL)*100) %>"
        },
        "Public Sector": {
          "formula": "<%= Math.round((DATA_ELEMENT_PUB/DATA_ELEMENT_PUB_TOTAL )*100) %>"
        }
      }
    }
  }
}
Clone this wiki locally