Observation Effect

The Observation effect provides a unified way to create and update clinical observations within the Canvas platform. Observations can include vitals (blood pressure, temperature, etc.), lab results, and other clinical measurements. The effect supports structured coding using standard terminologies (LOINC, SNOMED), components for multi-part measurements, and value codings for interpretation.

Attributes #

NameTypeDescription
observation_idstr or UUID or NoneUnique identifier of an existing observation. Must be unset when creating; required when updating.
patient_idstr or NoneID of the patient for this observation. Required when creating.
is_member_of_idstr or UUID or NoneReference to a parent observation (for grouping related observations).
categorystr or list[str] or NoneCategory of observation (e.g., “vital-signs”, “laboratory”, “imaging”). Can be a single category or a list of categories.
unitsstr or NoneUnit of measure for the observation value (e.g., “mmHg”, “mg/dL”).
valuestr or NoneThe observation value as a string.
note_idint or NoneID of the note associated with this observation.
namestr or NoneHuman-readable name for the observation. Required when creating.
effective_datetimedatetime or NoneDate and time when the observation was taken. Required when creating.
codingslist[CodingData] or NoneList of standardized codes identifying this observation (e.g., LOINC codes).
componentslist[ObservationComponentData] or NoneList of components for multi-part observations (e.g., systolic and diastolic BP).
value_codingslist[CodingData] or NoneList of coded values for interpretation (e.g., “normal”, “abnormal”).

Helper Classes #

CodingData #

Represents a standardized code from a terminology system (LOINC, SNOMED, etc.).

NameTypeDescription
codestrThe code value from the terminology system.
displaystrHuman-readable display text for the code.
systemstrURI identifying the terminology system (e.g., “http://loinc.org”).
versionstrVersion of the terminology system. Defaults to empty string.
user_selectedboolWhether this code was explicitly selected by the user. Defaults to False.

ObservationComponentData #

Represents a component of a multi-part observation (e.g., systolic and diastolic blood pressure).

NameTypeDescription
value_quantitystrThe numeric value of this component.
value_quantity_unitstrUnit of measure for this component value.
namestrName of this component.
codingslist[CodingData] or NoneStandardized codes identifying this component.

Validation & Errors #

Before any effect is emitted, the model runs these checks:

Create Validation #

  • observation_id must not be set (will be generated by the system)
  • patient_id is required
  • name is required
  • effective_datetime is required
  • If is_member_of_id is provided, the parent observation must exist

Update Validation #

  • observation_id is required and must reference an existing observation
  • All other fields are optional; only dirty (modified) fields are updated
  • If is_member_of_id is provided, the parent observation must exist

Effect Methods #

create() #

Create a new observation record.

  • Effect Type: CREATE_OBSERVATION
  • Payload: { "data": { patient_id, name, effective_datetime, ... } }

update() #

Update an existing observation.

  • Effect Type: UPDATE_OBSERVATION
  • Payload: { "data": { observation_id, <dirty_fields> } }
  • Only fields marked dirty (modified on the model) are included in the update.

Example Usage #

import datetime

from canvas_sdk.effects.observation import Observation, CodingData, ObservationComponentData
from canvas_sdk.v1.data.observation import Observation as ObservationModel
from canvas_sdk.v1.data.patient import Patient

patient = Patient.objects.first()

Create a Blood Pressure Observation #

# Create a blood pressure observation with components and codings
bp_observation = Observation(
    patient_id=patient.id,
    name="Blood Pressure",
    category="vital-signs",
    value="120/80",
    units="mmHg",
    effective_datetime=datetime.datetime.now(),
    codings=[
        CodingData(
            code="85354-9",
            display="Blood pressure panel with all children optional",
            system="http://loinc.org",
            version="2.73",
            user_selected=True,
        )
    ],
    components=[
        ObservationComponentData(
            value_quantity="120",
            value_quantity_unit="mmHg",
            name="Systolic Blood Pressure",
            codings=[
                CodingData(
                    code="8480-6",
                    display="Systolic blood pressure",
                    system="http://loinc.org",
                )
            ],
        ),
        ObservationComponentData(
            value_quantity="80",
            value_quantity_unit="mmHg",
            name="Diastolic Blood Pressure",
            codings=[
                CodingData(
                    code="8462-4",
                    display="Diastolic blood pressure",
                    system="http://loinc.org",
                )
            ],
        ),
    ],
    value_codings=[
        CodingData(
            code="normal",
            display="Normal",
            system="http://terminology.hl7.org/CodeSystem/v3-ObservationInterpretation",
        )
    ],
)

effect_create = bp_observation.create()

Update an Existing Observation #

# Find an existing observation
existing_obs = ObservationModel.objects.filter(patient_id=patient.id).first()

# Update the blood pressure values
updated_bp = Observation(
    observation_id=existing_obs.id,
    value="130/85",
    units="mmHg",
    components=[
        ObservationComponentData(
            value_quantity="130",
            value_quantity_unit="mmHg",
            name="Systolic Blood Pressure",
            codings=[
                CodingData(
                    code="8480-6",
                    display="Systolic blood pressure",
                    system="http://loinc.org",
                )
            ],
        ),
        ObservationComponentData(
            value_quantity="85",
            value_quantity_unit="mmHg",
            name="Diastolic Blood Pressure",
            codings=[
                CodingData(
                    code="8462-4",
                    display="Diastolic blood pressure",
                    system="http://loinc.org",
                )
            ],
        ),
    ],
)

effect_update = updated_bp.update()

Create a Simple Lab Result #

# Create a simple lab observation
glucose = Observation(
    patient_id=patient.id,
    name="Glucose",
    category="laboratory",
    value="95",
    units="mg/dL",
    effective_datetime=datetime.datetime.now(),
    codings=[
        CodingData(
            code="2339-0",
            display="Glucose [Mass/volume] in Blood",
            system="http://loinc.org",
        )
    ],
)

effect_create_lab = glucose.create()

Create an Observation with Multiple Categories #

# Create an observation that belongs to multiple categories
comprehensive_assessment = Observation(
    patient_id=patient.id,
    name="Comprehensive Physical Assessment",
    category=["vital-signs", "exam"],  # Multiple categories
    value="Normal",
    effective_datetime=datetime.datetime.now(),
    codings=[
        CodingData(
            code="29545-1",
            display="Physical examination",
            system="http://loinc.org",
        )
    ],
)

effect_create_multi = comprehensive_assessment.create()