Adds functionality to store demographic information on the server. See /wiki/spaces/MTB/pages/1934819724 and Demographic Survey Categories (somewhat outdated).
Currently, there is no type checking on demographic answer values, so all values are stored as strings. Type checking is a V2 goal.
Routes are available for both app-level and study-level demographics. The idea is that some basic demographics might be collected during app onboarding which is not associated with a particular study, and then additional demographics data might be collected by a particular study within the app.
POST /v5/studies/{studyId}/participants/{userId}/demographics
Save/overwrite all demographics for a user
Study-level, posted on the user’s behalf (by researcher, study coordinator)
POST /v5/studies/{studyId}/participants/self/demographics
Save/overwrite all demographics for a user
Study-level, posted by the user
POST /v3/participants/{userId}/demographics
Save/overwrite all demographics for a user
App-level, posted on the user’s behalf (by app admin)
POST /v3/participants/self/demographics
Save/overwrite all demographics for a user
App-level, posted by the user
POST /v5/studies/{studyId}/participants/{userId}/demographics/assessment
Save/overwrite all demographics for a user
Study-level, posted on the user’s behalf (by researcher, study coordinator)
Uses the assessment JSON model
POST /v5/studies/{studyId}/participants/self/demographics/assessment
Save/overwrite all demographics for a user
Study-level, posted by the user
Uses the assessment JSON model
POST /v3/participants/{userId}/demographics/assessment
Save/overwrite all demographics for a user
App-level, posted on the user’s behalf (by app admin)
Uses the assessment JSON model
POST /v3/participants/self/demographics/assessment
Save/overwrite all demographics for a user
App-level, posted by the user
Uses the assessment JSON model
DELETE /v5/studies/{studyId}/participants/{userId}/demographics/{demographicId}
Deletes a specific demographic (single category) for a particular user
Study-level, done by researcher or study coordinator
DELETE /v3/participants/{userId}/demographics/{demographicId}
Deletes a specific demographic (single category) for a particular user
App-level, done by app admin
DELETE /v5/studies/{studyId}/participants/{userId}/demographics
Deletes all of a user’s demographics
Study-level, done by researcher or study coordinator
DELETE /v3/participants/{userId}/demographics
Deletes all of a user’s demographics
App-level, done by app admin
GET /v5/studies/{studyId}/participants/{userId}/demographics
Fetches all demographics for a user
Study-level, done by researcher/study-coordinator
GET /v3/participants/{userId}/demographics
Fetches all demographics for a user
App-level, done by app admin
GET /v5/studies/{studyId}/participants/demographics
Fetches all study-level demographics for all users within a study
Study-level, done by researcher/study-coordinator
GET /v3/participants/demographics
Fetches all app-level demographics for all users within an app
App-level, done by app admin
Unless otherwise stated, routes use the following schema (for a single user):
{ "userId": (read only) <string>, "demographics": { (category name): { "id": (guid) (read only) <string>, "multipleSelect": <bool>, "values": [ <any> ] "units": <string> }, ... } } |
Example:
{ "userId": "userId1", "demographics": { "height": { "id": "guid1", "multipleSelect": false, "values": [ 72.0 ] "units": "m" }, "race": { "id": "guid2", "multipleSelect": true, "values": [ "Asian", "Native Hawaiian or Other Pacific Islander" ] } } } |
Certain routes use an assessment response model for demographic input:
{ "stepHistory": [ { "children": [ { "identifier": <string> (category name), "answerType": { (optional) "unit": <string> }, "value": <single value, any type OR array of single value, any type OR object with fields of single value, any type> } ] } ] } |
Although other fields may be included in the normal assessment model, all other fields are discarded for demographics use.
Example:
{ "stepHistory": [ { "children": [ { "identifier": "height", "answerType": { "unit": "cm" }, "value": 72.0 }, { "identifier": "race", "value": [ "Asian", "Native Hawaiian or Other Pacific Islander" ] } ] } ] } |
SQL:
CREATE TABLE IF NOT EXISTS `DemographicsUsers` ( `id` varchar(60) NOT NULL, `studyId` varchar(60) NULL, `appId` varchar(60) NOT NULL, `userId` varchar(255) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY (`studyId`, `appId`, `userId`), CONSTRAINT `DemographicUser-Account-Constraint` FOREIGN KEY (`userId`) REFERENCES `Accounts` (`id`) ON DELETE CASCADE, CONSTRAINT `DemographicUser-Study-Constraint` FOREIGN KEY (`studyId`, `appId`) REFERENCES `Substudies` (`id`, `studyId`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE IF NOT EXISTS `Demographics` ( `id` varchar(60) NOT NULL, `demographicUserId` varchar(60) NOT NULL, `categoryName` varchar(255) NOT NULL, `multipleSelect` boolean NOT NULL, `units` varchar(512) NULL, PRIMARY KEY (`id`), UNIQUE KEY (`demographicUserId`, `categoryName`), CONSTRAINT `Demographic-DemographicUser-Constraint` FOREIGN KEY (`demographicUserId`) REFERENCES `DemographicsUsers` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; CREATE TABLE IF NOT EXISTS `DemographicsValues` ( `demographicId` varchar(60) NOT NULL, `value` varchar(1024) NOT NULL, CONSTRAINT `DemographicValue-Demographic-Constraint` FOREIGN KEY (`demographicId`) REFERENCES `Demographics` (`id`) ON DELETE CASCADE ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci; |
3 tables are used because each table has a one-to-many relationship with the table below it, both intuitively and in SQL, and this nested format is much simpler with multiple tables. It also allows convenient grouping by userId for a better JSON format.
DemographicsUsers
: maps to Java class DemographicUser
Contains all demographics for a single user
One-to-many with Demographics
Demographics
: maps to Java class Demographic
Contains all values for a single demographic category for a single user
One-to-many with DemographicsValues
DemographicsValues
: maps to Java class DemographicValue
Contains a single value in a demographic category
App-level demographics are represented with a null studyId.