-
Notifications
You must be signed in to change notification settings - Fork 1
d2‐autogen‐forms
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.
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
}
}
}
}
}
}
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 | |
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 | |
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 | | | |
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 | | | | |
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
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.
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.
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".
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 tofalse
, the data elementsDATA_ELEMENT_CODE_1
andDATA_ELEMENT_CODE_2
will be disabled. - When the value of
DATA_ELEMENT_CODE
is equal to false, the data element with codeDATA_ELEMENT_CODE
is hidden.
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"
}
}
}
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 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"
}
}
This section property customizes the style of the section name:
{
"titleVariant": "h1" | "h2" | "h3" | "h4" | "h5" | "h6"
}
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
.
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
- 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 thetexts
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 thedataElements
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) %>"
}
}
}
}
}