Skip to content

Form Responses

Form Responses are a part of Reader Tasks, which is a feature available with a Flywheel Clinical module.

A form response is the structured data submitted by a reader when completing a reader task. Responses are generated when the assignee fills in and submits the form defined by the task's protocol.

Responses can be saved multiple times as drafts before submission. Validation is only enforced at submission time. Once a response is submitted, it is locked: no further edits are possible.

Form response versions: Legacy vs Tasks Manager

Flywheel has two distinct form response systems, corresponding to the two reader task systems. They are not compatible with each other, and are accessed in different ways. If you are unsure which version your instance has, contact your Flywheel administrator or check your site settings with the SDK.

Check whether Legacy tasks are enabled
fw.get_config().features.get("reader_tasks")
Check whether Tasks Manager is enabled
fw.get_config().features.get("tasks_refactor")

Legacy tasks are not managed through SDK methods, but can be managed through Low-Level API Access or the fw-client Python package for making API calls.

Form response fields

Field Description
id Unique identifier for the response
task_id ID of the task this response is linked to
protocol_id ID of the protocol whose form schema was used
data Dict containing the reader's submitted field values
submitted True if the response has been submitted and locked
submitted_at Timestamp of submission (only set if submitted is True)
origin The user who submitted the response
parents References to parent containers (subject, session, etc.)
revision Integer revision count (increments on each save)
created Creation timestamp
modified Last-modified timestamp

The data field contains a dict whose keys match the key properties of the protocol's form fields, and whose values are the reader's input:

Example response data structure
# For a protocol with fields keyed "finding" and "impression":
response.data == {
    "finding": "abnormal",
    "impression": "Patchy opacity noted in the right lower lobe."
}

List responses for a task

fw.list_task_responses(task_id) returns all form responses associated with a given task. A task typically has one response, but the endpoint supports pagination for edge cases.

List form responses for a task
result = fw.list_task_responses(task_id)
for response in result.results:
    print(f"{response.id}: submitted={response.submitted}")

Get a response by ID

fw.get_response(response_id) retrieves a single response by its ID.

Get a form response by ID
response = fw.get_response(response_id)
print(response.data)
print(response.submitted)

Access submitted field values

The data dict maps protocol field keys to the reader's input values. Access individual fields by key name.

Access individual field values from a response
response = fw.get_response(response_id)

finding = response.data.get("finding")
impression = response.data.get("impression")
print(f"Finding: {finding}")
print(f"Impression: {impression}")

Create or replace a response

fw.create_or_replace_task_response(task_id, body) creates a new response for a task, or replaces an existing unsubmitted response. If a submitted response already exists, the call returns a 409 conflict error. Only the task assignee can create or replace responses.

Responses can be saved multiple times as drafts (without validation) using this method. Validation is enforced only when submitting.

Save a draft response
response = fw.create_or_replace_task_response(
    task_id,
    {
        "task_id": task_id,
        "data": {
            "finding": "normal",
            "impression": "No acute cardiopulmonary abnormality.",
        },
    },
)

Update an existing response

fw.update_response(response_id, body) performs a partial update on an unsubmitted response. This is useful for saving incremental draft changes. Submitted responses cannot be updated.

Update a draft response
updated = fw.update_response(
    response_id, {"data": {"finding": "abnormal", "impression": "Revised: Patchy opacity noted."}}
)

Submit a response

fw.submit_response(response_id) validates the response against the protocol's form schema and locks it. Once submitted:

  • submitted is set to True
  • submitted_at is recorded
  • origin is set to the submitting user
  • Further edits are disabled
Submit a form response
submitted = fw.submit_response(response_id)
print(submitted.submitted)  # True
print(submitted.submitted_at)  # datetime of submission

Aggregate responses across tasks

A common use case is collecting form responses from multiple tasks for downstream analysis. Iterate through tasks and retrieve the submitted response for each.

Collect all submitted responses for a protocol
import pandas as pd

result = fw.find_all_api_tasks_reader_get(filter=f"protocol_id={protocol_id},status=completed")

records = []
for task in result.results:
    responses = fw.list_task_responses(task.id)
    for response in responses.results:
        if response.submitted:
            record = {"task_id": task.id}
            record.update(response.data or {})
            records.append(record)

df = pd.DataFrame(records)