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 #
| Name | Type | Description |
|---|---|---|
observation_id | str or UUID or None | Unique identifier of an existing observation. Must be unset when creating; required when updating. |
patient_id | str or None | ID of the patient for this observation. Required when creating. |
is_member_of_id | str or UUID or None | Reference to a parent observation (for grouping related observations). |
category | str or list[str] or None | Category of observation (e.g., “vital-signs”, “laboratory”, “imaging”). Can be a single category or a list of categories. |
units | str or None | Unit of measure for the observation value (e.g., “mmHg”, “mg/dL”). |
value | str or None | The observation value as a string. |
note_id | int or None | ID of the note associated with this observation. |
name | str or None | Human-readable name for the observation. Required when creating. |
effective_datetime | datetime or None | Date and time when the observation was taken. Required when creating. |
codings | list[CodingData] or None | List of standardized codes identifying this observation (e.g., LOINC codes). |
components | list[ObservationComponentData] or None | List of components for multi-part observations (e.g., systolic and diastolic BP). |
value_codings | list[CodingData] or None | List of coded values for interpretation (e.g., “normal”, “abnormal”). |
Helper Classes #
CodingData #
Represents a standardized code from a terminology system (LOINC, SNOMED, etc.).
| Name | Type | Description |
|---|---|---|
code | str | The code value from the terminology system. |
display | str | Human-readable display text for the code. |
system | str | URI identifying the terminology system (e.g., “http://loinc.org”). |
version | str | Version of the terminology system. Defaults to empty string. |
user_selected | bool | Whether 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).
| Name | Type | Description |
|---|---|---|
value_quantity | str | The numeric value of this component. |
value_quantity_unit | str | Unit of measure for this component value. |
name | str | Name of this component. |
codings | list[CodingData] or None | Standardized 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()