Skip to content
CARP Docs
Ask AISystemLightDark
On this page

Survey data

The dk.cachet.carp.survey measurement and the Research Package results, answers and timestamps inside it.

Copy MarkdownOpen inOpen in GitHubView as MarkdownOpen in Scira AIOpen in ChatGPTOpen in ClaudeOpen in Cursor

Each run of an RPAppTask produces one dk.cachet.carp.survey measurement, when the participant submits the task or leaves it. Its data, an RPTaskResultData with the __type dk.cachet.carp.survey, holds the complete Research Package result. For the measurement envelope around the data, see Measurements and data.

Measurements per task

A run of an RPAppTask can produce these measurements:

Measure typeWhenData
dk.cachet.carp.surveyThe participant submits or leaves the taskRPTaskResultData with the answers
dk.cachet.carp.completedapptask.<type>The participant submits the taskCompletedAppTask, which holds the same RPTaskResultData
The types in the task's measuresBetween onStart() and the end of the taskThe data of each measure

<type> is the type of the RPAppTask: survey, cognition or informed_consent. A submitted result is therefore stored twice. Count runs and read answers from dk.cachet.carp.survey, which also holds the canceled runs.

Survey result

RPTaskResultData has two fields:

FieldTypeDescription
statusSurveyStatussubmitted or canceled. unknown appears only when JSON without a status is read back.
resultRPTaskResult?The Research Package result. For a canceled task, the answers given until the participant left. Omitted from the JSON when null.

The RPTaskResult, with the __type RPTaskResult, holds the results of the steps:

FieldTypeDescription
identifierStringThe identifier of the Research Package task, for example who5_task
startDateDateTime?When the survey opened
endDateDateTime?When the participant finished the last step. Not set in a canceled result.
resultsMap<String, RPResult>One result for each step that produces one, keyed by step identifier

Step results

These steps produce results; instruction, timer and completion steps leave nothing in the result:

StepResult classContent
RPQuestionStepRPStepResultThe answer under the key answer
RPFormStepRPStepResultOne nested RPStepResult for each question, keyed by question identifier
RPActivityStep, including the cognitive testsRPActivityResultThe test's values under the key result, with step times and interactions
RPConsentReviewStepRPConsentSignatureResultThe consent document as shown, and the participant's name and signature
RPVisualConsentStep with askPermission: trueRPPermissionResultThe status of each permission asked for

An RPStepResult has these fields:

FieldTypeDescription
identifierStringThe step identifier
startDateDateTime?When the step was shown
endDateDateTime?When the answer last changed
questionTitleStringThe step's title, as written in the task
resultsMap<String, dynamic>The answer under the key answer (RPStepResult.DEFAULT_KEY)
answerFormatRPAnswerFormatA copy of the answer format, including every choice

An RPActivityResult has these fields:

FieldTypeDescription
identifierStringThe step identifier
endDateDateTime?When the test last reported a value. startDate is never set.
resultsMap<String, dynamic>The test's values under the key result. For the keys of each test, see Read the test results.
stepTimesStepTimesThe times of the phases, under the keys instruction_started, instruction_ended, test_shown, test_started, test_ended, results_shown and results_closed. Keys for phases that a test does not log are left out; the Cognition Package tests never log test_shown or results_closed.
interactionsList<Interaction>One entry for each interaction that the test logs, such as a tap or a drawn path, with time, correctness (Correct gesture or Wrong gesture), type and description

The consent signature result holds the participant's first and last name and the signature image. The image is a string of the PNG bytes in decimal, such as "[137, 80, 78, 71, ...]", not Base64.

For the fields of the consent result, see Read the consent result. For the permission result, see Read the permission statuses.

Answer values

The value under answer depends on the answer format:

Answer formatValue of answerJSON example
RPChoiceAnswerFormatList<RPChoice> with the selected choices, also for a single choice[{"__type": "RPChoice", "text": "Most of the time", "value": 4, "isFreeText": false}]
RPIntegerAnswerFormatString"7"
RPDoubleAnswerFormatString"5.4"
RPSliderAnswerFormatdouble6.0
RPTextAnswerFormatString"Woke up twice"
RPDateTimeAnswerFormatString from DateTime.toString(), in local time"2026-10-26 07:30:00.000"
RPImageChoiceAnswerFormatRPImageChoice{"__type": "RPImageChoice", "imageUrl": "assets/images/happy.png", "value": 4, "description": "Happy"}
An optional question that was skippednullnull

Analyze the choice value, not the text. With RPLocalizations.delegate in the app, the submitted result holds translated choice texts. A free-text choice holds the text that the participant typed.

Timestamps

TimestampFormatMeaning
sensorStartTimeMicroseconds since epochWhen the participant submitted or left the task
startDate, endDate of the taskISO 8601 local time without UTC offsetWhen the survey opened and when the participant finished it
startDate, endDate of a stepISO 8601 local time without UTC offsetWhen the step was shown and when its answer last changed
completedAtISO 8601 UTCWhen CARP Mobile Sensing (CAMS) marked the task as done

With RPLocalizations.delegate in the app, the submitted result is a translated copy in which each RPStepResult is created again at submit time. Its startDate and endDate are then the submit time, not the time of the answer. The task dates and canceled results keep the real times.

The local times carry no UTC offset. To convert them, collect the phone's time zone, for example with the DeviceSamplingPackage.TIMEZONE measure.

Canceled surveys

Canceled answers are stored

When the participant leaves a survey, the package stores the answers given so far, although the dialog says "Your answers will not be saved." Say so in your consent text, or change the dialog text with the discard_confirmation_body translation key.

A canceled result has the status canceled and no endDate, and CAMS adds no completed app task measurement for it. The task stays available, so a participant who starts it again produces another measurement for the same task. For the translation keys, see Change the package texts.

Completed app task

When a task is done, CAMS adds a CompletedAppTask measurement with the __type dk.cachet.carp.completedapptask.<type>:

FieldTypeDescription
taskNameStringThe name of the RPAppTask
taskDataData?The same RPTaskResultData as in the survey measurement. Omitted when null.
taskTypeStringsurvey, cognition or informed_consent
completedAtDateTimeWhen the task was marked as done, in UTC

CAMS adds this measurement in every study deployed on the phone, also for the tasks of other studies.

The survey data does not name the RPAppTask. To tell results apart:

  • give every Research Package task its own identifier, which the result repeats in result.identifier;
  • give every RPAppTask a unique name, which the completed app task repeats in taskName for submitted tasks.

Measurement.taskControl does not help here: it is not serialized, and it is null for a task that CAMS restored after an app restart.

Read results in the app

Filter the measurements on RPTaskResultData and read the answers from the step results. This example sums the choice values of a submitted WHO-5 result:

import 'package:research_package/research_package.dart';

/// The sum of the selected choice values in a task result
int choiceTotal(RPTaskResult result) {
  var total = 0;
  for (final stepResult in result.results.values) {
    if (stepResult is RPStepResult) {
      final answer = stepResult.results[RPStepResult.DEFAULT_KEY];
      if (answer is List) {
        for (final choice in answer.whereType<RPChoice>()) {
          total += choice.value;
        }
      }
    }
  }
  return total;
}

void printWho5Totals() {
  SmartPhoneClientManager().measurements
      .where((measurement) => measurement.data is RPTaskResultData)
      .listen((measurement) {
        final data = measurement.data as RPTaskResultData;
        final result = data.result;
        if (data.status == SurveyStatus.submitted &&
            result?.identifier == 'who5_task') {
          print('WHO-5 total: ${choiceTotal(result!)}');
        }
      });
}

Test for List, not List<RPChoice>: with RPLocalizations.delegate in the app, the submitted answer is a List<dynamic> of RPChoice objects. After a task is done, userTask.result holds the same RPTaskResultData. A result decoded from JSON holds raw maps: there, a choice answer is a list of maps with text and value keys.

Example data

Submitted survey

A submitted WHO-5 survey, with four of the five question results left out:

{
  "__type": "dk.cachet.carp.survey",
  "status": "submitted",
  "result": {
    "__type": "RPTaskResult",
    "identifier": "who5_task",
    "startDate": "2026-10-26T13:02:05.412907",
    "endDate": "2026-10-26T13:02:41.230518",
    "results": {
      "who5_question_1": {
        "__type": "RPStepResult",
        "identifier": "who5_question_1",
        "startDate": "2026-10-26T13:02:05.498120",
        "endDate": "2026-10-26T13:02:09.871244",
        "questionTitle": "I have felt cheerful and in good spirits",
        "results": {
          "answer": [
            {
              "__type": "RPChoice",
              "text": "Most of the time",
              "value": 4,
              "isFreeText": false
            }
          ]
        },
        "answerFormat": {
          "__type": "RPChoiceAnswerFormat",
          "questionType": "SingleChoice",
          "choices": [
            { "__type": "RPChoice", "text": "All of the time", "value": 5, "isFreeText": false },
            { "__type": "RPChoice", "text": "Most of the time", "value": 4, "isFreeText": false },
            { "__type": "RPChoice", "text": "More than half of the time", "value": 3, "isFreeText": false },
            { "__type": "RPChoice", "text": "Less than half of the time", "value": 2, "isFreeText": false },
            { "__type": "RPChoice", "text": "Some of the time", "value": 1, "isFreeText": false },
            { "__type": "RPChoice", "text": "At no time", "value": 0, "isFreeText": false }
          ],
          "answerStyle": "SingleChoice"
        }
      }
    }
  }
}
Canceled survey

The sleep diary from Building surveys, left after the first question:

{
  "__type": "dk.cachet.carp.survey",
  "status": "canceled",
  "result": {
    "__type": "RPTaskResult",
    "identifier": "sleep_diary",
    "startDate": "2026-10-27T08:12:40.118326",
    "results": {
      "sleep_hours": {
        "__type": "RPStepResult",
        "identifier": "sleep_hours",
        "startDate": "2026-10-27T08:12:43.502114",
        "endDate": "2026-10-27T08:12:47.660891",
        "questionTitle": "How many hours did you sleep?",
        "results": {
          "answer": "7"
        },
        "answerFormat": {
          "__type": "RPIntegerAnswerFormat",
          "minValue": 0,
          "maxValue": 24,
          "suffix": "hours",
          "questionType": "Integer"
        }
      }
    }
  }
}
Cognitive test result

The entry of an RPTappingActivity with the identifier tapping in results, with the first two of its 57 interactions:

{
  "__type": "RPActivityResult",
  "identifier": "tapping",
  "endDate": "2026-10-27T18:04:26.551208",
  "results": {
    "result": {
      "Total taps": 57
    }
  },
  "stepTimes": {
    "instruction_started": "2026-10-27T18:03:51.902117",
    "instruction_ended": "2026-10-27T18:04:03.418532",
    "test_started": "2026-10-27T18:04:03.418611",
    "test_ended": "2026-10-27T18:04:26.551040",
    "results_shown": "2026-10-27T18:04:26.551349"
  },
  "interactions": [
    {
      "time": "2026-10-27T18:04:06.477215",
      "correctness": "Correct gesture",
      "type": "Button tap",
      "description": "Pressed the left button"
    },
    {
      "time": "2026-10-27T18:04:06.702390",
      "correctness": "Correct gesture",
      "type": "Button tap",
      "description": "Pressed the right button"
    }
  ]
}
Completed app task

The completed app task of a one-question mood survey with a slider:

{
  "__type": "dk.cachet.carp.completedapptask.survey",
  "taskName": "Mood check",
  "taskData": {
    "__type": "dk.cachet.carp.survey",
    "status": "submitted",
    "result": {
      "__type": "RPTaskResult",
      "identifier": "mood_check",
      "startDate": "2026-10-27T15:30:02.770114",
      "endDate": "2026-10-27T15:30:09.315827",
      "results": {
        "mood": {
          "__type": "RPStepResult",
          "identifier": "mood",
          "startDate": "2026-10-27T15:30:02.853690",
          "endDate": "2026-10-27T15:30:06.120455",
          "questionTitle": "How is your mood right now?",
          "results": {
            "answer": 6.0
          },
          "answerFormat": {
            "__type": "RPSliderAnswerFormat",
            "minValue": 0.0,
            "maxValue": 10.0,
            "divisions": 10,
            "prefix": "",
            "suffix": "",
            "questionType": "Scale"
          }
        }
      }
    }
  },
  "taskType": "survey",
  "completedAt": "2026-10-27T14:30:09.322671Z"
}

Quick links

Page · DocumentationCARP DocumentationPage · DocumentationIntroductionPage · DocumentationQuickstartPage · DocumentationTerminologyPage · DocumentationArchitecture