Skip to content

Creating Form Definitions for Campaigns

Maté Strysewske edited this page Apr 6, 2021 · 1 revision

Campaign Form Definition

The Campaign module of SORMAS supports forms that can be defined using JSON and that are dynamically built based on these definitions. As there currently is no user interface to save these definitions, they need to be manually added to the campaignforms table in the database:

INSERT INTO campaignformmeta(id, uuid, changedate, creationdate, languagecode, formid, formname)
	VALUES (nextval('entity_seq'), overlay(overlay(overlay(
		substring(upper(REPLACE(CAST(CAST(md5(CAST(random() AS text) || CAST(clock_timestamp() AS text)) AS uuid) AS text), '-', '')), 0, 30) 
		placing '-' from 7) placing '-' from 14) placing '-' from 21), now(), now(), 'en', '???', '???');

A basic campaign form definition containing two fields would look like this and need to be inserted into the 'campaignformelements' column.

[
  {
    "type": "text",
    "id": "firstFieldId",
    "caption": "Caption of the first field"
  },
  {
    "type": "number",
    "id": "secondFieldId",
    "caption": "Caption of the second field"
  }
]

Fields can be customized with a number of attributes that, in turn, support a variety of potential values:

"type"

The type defines the basic functionalities of the field: whether it expects user input, which kind of input it supports and how it looks like. This attribute must be specified for all fields. The supported values are:

section: Marks the beginning of a new section. This will add a background color to all fields within the section. Starting a new section will end the previous one. Background colors alternate between a lighter and a darker grey.
label: A text label that can be used for descriptions or instructions. Does not support user input.
text: A text field that allows input without restrictions regarding the entered characters.
number: A text field that only allows entering numbers.
yes-no: A field that allows users to select between two options ("Yes" and "No").

"id"

The ID of the field that is used to reference it in other fields and to provide translations. This attribute must be specified for all fields and has to be unique within the form.

"caption"

The default caption for the field. Supports basic HTML tags, e.g. headline tags, line breaks, bold, italic, underline, etc.

"important"

Flags important fields that should be entered by the users and are shown in the overview list of campaign form data. (e.g. "important": true)

"styles"

Optional attribute that can be used to change the visual appearance of the field. Multiple styles can be specified at once. The expected notation is ["style1", "style2"]. Campaign forms use a grid system with 12 columns. By default, text and number fields take 4 columns (so 1/3rd of the width) and yes-no fields take the full width of a row. The supported values are:

inline: Displays the field in line with others. This is the default for text and number fields.
row: Makes the field take the whole width of the row. This is the default for yes-no fields.
first: Pushes the field to the next row and makes it the first element in that row.
col-1, col-2, ..., col-12: Defines how many columns the field should take. If the amount of columns would exceed the maximum number of columns for the current row, the field is pushed to the next row.

"dependingOn" and "dependingOnValues"

Allows to make the visibility of the field conditional on the value of another field in the same form. dependingOn expects the ID of the other field, dependingOnValues expects a list of values. If the field specified in dependingOn contains one of these values, the field for which this attribute is specified is made visible; otherwise it's hidden. The expected notation for dependingOnValues is ["dependingOnValues": ["yes"].

"expression"

Campaign data forms can also have fields (expression="... SpEL expression ...") that are calculated based on the number entered for e.g. "Missed children based on recall" (>=2) and "Number of houses team did not visit" (>=2). An expression can contain math operators (+, -, ..) and logic operators (e.g. ''missedChrildren > 2'').

[
  {
	"type": "number",
	"id": "missedChildren",
	"caption": "Total number of missed children (based on recall and FM)",
	"styles": ["row"],
	"important": true
  },
  {
	"type": "number",
	"id": "teamDidNotVisit",
	"caption": "Not visited by team",
	"styles": ["col-6"]
  },
  {
	"type": "yes-no",
	"id": "poorlyCoveredArea",
        "expression": "missedChildren > 2 and teamDidNotVisit >= 2",
	"caption": "Poorly covered area (2 or more children missed based on finger mark)?",
	"important": true
  }
]
[
  {
	"type": "number",
	"id": "childrenSeenByMonitor",
	"caption": "Number of < 5 years children seen by the monitor",
	"styles": ["row", "col-3"]
  },
  {
	"type": "number",
	"id": "childrenFingerMarking",
        "expression": "childrenSeenByMonitor + 5",
	"caption": "Number of < 5 years children seen with finger marking by monitor",
	"styles": ["row", "col-3"]
  }
]

See Spring Expression Language (SpEL) for more information.

Translations

Campaign forms can be translated to every language that is supported by SORMAS. To do this, JSON needs to be specified in the campaignformtranslations column. A basic translation definition would look like this:

[
  {
    "languageCode": "de_DE",
    "translations": [
      {
        "elementId": "firstFieldId",
        "caption": "German translation of the caption"
      },
      {
        "elementId": "secondFieldId",
        "caption": "German translation of the caption"
      }
    ]
  }
]

The languageCode attribute defines the locale the translation is for and needs to match one of the locales supported by SORMAS. The translations attribute expects a list of translations, each of which need to specify the elementId, which is the id of the translated field in the form, and a translated caption that supports basic HTML elements.

Examples

An example JSON definition to collect data on missed children for vaccination campaigns:

[{
	"type": "section",
	"id": "totalNumbersSection"
}, {
	"type": "label",
	"id": "totalNumbersLabel",
	"caption": "<h3>Total Numbers</h3>"
}, {
	"type": "number",
	"id": "childrenLivingInHouses",
	"caption": "Number of < 5 years children living in houses",
	"styles": ["row", "col-3"],
	"important": true
}, {
	"type": "number",
	"id": "childrenVaccinatedRecall",
	"caption": "Number of < 5 years children vaccinated based on recall",
	"styles": ["row", "col-3"]
}, {
	"type": "number",
	"id": "childrenSeenByMonitor",
	"caption": "Number of < 5 years children seen by the monitor",
	"styles": ["row", "col-3"]
}, {
	"type": "number",
	"id": "childrenFingerMarking",
	"caption": "Number of < 5 years children seen with finger marking by monitor",
	"styles": ["row", "col-3"]
}, {
	"type": "section",
	"id": "missedChildrenSection"
}, {
	"type": "label",
	"id": "missedChildrenLabel",
	"caption": "<h3>Missed Children</h3>"
}, {
	"type": "number",
	"id": "missedChildren",
	"caption": "Total number of missed children (based on recall and FM)",
	"styles": ["row"],
	"important": true
}, {
	"type": "label",
	"id": "missedChildrenNumbers",
	"caption": "<h4>Number of missed children for the following reasons:</h4>",
	"styles": ["col-6"]
}, {
	"type": "number",
	"id": "houseNotIncluded",
	"caption": "House not included in micro plan",
	"styles": ["col-6"]
}, {
	"type": "number",
	"id": "teamDidNotVisit",
	"caption": "Not visited by team",
	"styles": ["col-6"]
}, {
	"type": "number",
	"id": "poorScreening",
	"caption": "Poor screening by team",
	"styles": ["col-6"]
}, {
	"type": "number",
	"id": "childAbsent",
	"caption": "Children absent",
	"styles": ["col-6"]
}, {
	"type": "number",
	"id": "newbornSickSleep",
	"caption": "Newborn, Sick, Sleep",
	"styles": ["col-6"]
}, {
	"type": "number",
	"id": "refusal",
	"caption": "Refusal",
	"styles": ["col-6"]
}, {
	"type": "number",
	"id": "guest",
	"caption": "Guest",
	"styles": ["col-6"],
	"dependingOn": "refusal",
	"dependingOnValues": [1, 5]
}, {
	"type": "section",
	"id": "doorMarkingsSection"
}, {
	"type": "label",
	"id": "doorMarkingsLabel",
	"caption": "<h3>Door Markings</h3>"
}, {
	"type": "number",
	"id": "doorMarkingCorrect",
	"caption": "Correct"
}, {
	"type": "number",
	"id": "doorMarkingIncorrect",
	"caption": "Incorrect"
}, {
	"type": "number",
	"id": "doorMarkingNotMarked",
	"caption": "Not marked"
}, {
	"type": "section",
	"id": "areaSection"
}, {
	"type": "yes-no",
	"id": "poorlyCoveredArea",
	"caption": "Poorly covered area (2 or more children missed based on finger mark)?",
	"important": true
}, {
	"type": "yes-no",
	"id": "missedArea",
	"caption": "Missed area (2 or more houses missed due to no team visit)?",
	"dependingOn": "poorlyCoveredArea",
	"dependingOnValues": ["YES"]
}, {
	"type": "text",
	"id": "comment",
	"caption": "Comment",
	"styles": ["col-12"],
	"dependingOn": "missedArea",
	"dependingOnValues": ["yes"]
}]