← Knowledge library

Clinical care

Observation Definition

An ObservationDefinition is master data describing how a particular observation is captured — its code, permitted data type, unit, method, body site, and reference ranges. You create one when you need a measurement collected consistently across…

3.1 · versioned documentation · 8 min read

An ObservationDefinition is master data describing how a particular observation is captured — its code, permitted data type, unit, method, body site, and (interpretation) reference ranges. You create one when you need a measurement collected consistently across forms; questionnaires reference it so the same observation (blood pressure, say) is recorded the same way everywhere, whether instance-wide or within a single facility. One observation code can back several definitions. The model loosely follows the FHIR ObservationDefinition resource.

The Django model is only storage: code, body_site, method, permitted_unit, component, and qualified_ranges are opaque JSONFields. The shape you write against — enums, value-set bindings, read/write contracts — lives in the Pydantic resource specs (care/emr/resources/observation_definition/spec.py), built on the EMRResource base.

Source:

Models

Model Purpose
ObservationDefinition Master-data definition of how an observation is coded, typed, and measured

ObservationDefinition extends SlugBaseModel, which adds facility-scoped slug helpers on top of EMRBaseModel. From EMRBaseModel it inherits external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and the history/meta JSON fields.

ObservationDefinition fields

Identity & scope

Field Type Notes
facility FK → Facility (PROTECT) Nullable, default=None. Set means the definition is scoped to that facility; null means instance-wide. Excluded from the base spec (__exclude__ = ["facility"]) and set server-side from the facility UUID on create
version IntegerField Defaults to 1. Read-only — appears in ...ReadSpec only, never accepted on write
slug CharField(255) Set server-side from slug_value, then namespaced. See slug behaviour
title CharField(1024) Required. Display name shown when authoring questionnaires; not the observation code
status CharField(255) Required. Lifecycle code from ObservationStatusChoices — see Status values. Definitions move to retired rather than being destroyed
description TextField Required. Natural-language (markdown) description
derived_from_uri TextField External URI this definition was derived from. Optional (str | None, default None)

Coding & measurement

Field Type Notes
category CharField(255) Nullable in the model, but required in the API as ObservationCategoryChoices — see Category values
code JSONField Required. What is being observed: a Coding bound to the system-observation value set (LOINC) — see Coding shape
permitted_data_type CharField(100) Required. QuestionType enum (questionnaire data types) — see Permitted data type values. group, display, and url are rejected
body_site JSONField Nullable. Coding bound to the system-body-site-observation value set (SNOMED CT)
method JSONField Nullable. Coding bound to the system-collection-method value set (SNOMED CT)
permitted_unit JSONField Nullable. Coding bound to the system-ucum-units value set (UCUM). MANSOQ does not convert between units when displaying data

Components & ranges

Field Type Notes
component JSONField Nullable. list[ObservationDefinitionComponentSpec] | None. Several measured values under one definition (systolic + diastolic, for example), each with its own code, permitted_data_type, permitted_unit, and qualified_ranges — see ObservationDefinitionComponentSpec
qualified_ranges JSONField Model default list. Required in the API as list[QualifiedRangeSpec], though it may be empty. Condition-dependent reference ranges / interpretations — see QualifiedRangeSpec

Enum values

Status values

ObservationStatusChoices — bound to the status field.

Value
draft
active
retired
unknown

Category values

ObservationCategoryChoices — bound to the category field.

Value
social_history
vital_signs
imaging
laboratory
procedure
survey
exam
therapy
activity

Permitted data type values

QuestionType (from questionnaire/spec.py) — bound to permitted_data_type on both the definition and each component. validate_question_type rejects group, display, and url with "Cannot create a definition with this type".

Value Allowed for a definition
group No (rejected)
boolean Yes
decimal Yes
integer Yes
string Yes
text Yes
display No (rejected)
date Yes
dateTime Yes
time Yes
choice Yes
url No (rejected)
quantity Yes
structured Yes

JSON field shapes (nested specs)

Coding shape

The coded fields (code, body_site, method, permitted_unit) store a Coding object. The spec binds each to a value set via ValueSetBoundCoding[<slug>], so the submitted code is validated against that value set on write.

Coding {
  system:  str | null
  version: str | null
  code:    str            # required
  display: str | null
}                          # extra="forbid" — unknown keys rejected
Field Bound value set (slug) Code system
code system-observation LOINC (http://loinc.org)
body_site system-body-site-observation SNOMED CT (http://snomed.info/sct)
method system-collection-method SNOMED CT
permitted_unit system-ucum-units UCUM (http://unitsofmeasure.org)

ObservationDefinitionComponentSpec

Element of the component list — one per measured value when a definition carries several.

ObservationDefinitionComponentSpec {
  code:                ValueSetBoundCoding[system-observation]   # required
  permitted_data_type: QuestionType                              # required; group/display/url rejected
  permitted_unit:      ValueSetBoundCoding[system-ucum-units] | null = null
  qualified_ranges:    list[QualifiedRangeSpec]                  # required
}

QualifiedRangeSpec

Element of qualified_ranges (on the definition and on each component). A qualified range is either numeric (ranges) or categorical (coded value sets) — never both.

QualifiedRangeSpec {
  title:                    str | null = null
  conditions:               list[EvaluatorConditionSpec] = []
  ranges:                   list[NumericRangeSpec] = []
  default_interpretation:   InterpretationSpec | null = null
  normal_coded_value_set:   str | null = ""
  critical_coded_value_set: str | null = ""
  abnormal_coded_value_set: str | null = ""
  valueset_interpretation:  list[ValueSetInterpretationSpec] = []
}

validate_categorical_or_numeric enforces:

NumericRangeSpec

NumericRangeSpec {
  interpretation: InterpretationSpec          # required
  min: Decimal(max_digits=20, decimal_places=6) | null = null
  max: Decimal(max_digits=20, decimal_places=6) | null = null
}

At least one of min/max must be provided (validate_range).

InterpretationSpec

InterpretationSpec {
  display:   str             # required
  icon:      str | null = ""
  color:     str | null = ""
  highlight: bool | null = false
  code:      Coding | null = {}
}

ValueSetInterpretationSpec

ValueSetInterpretationSpec {
  interpretation: InterpretationSpec   # required
  valuset:        str                  # value-set slug (note: spelling "valuset")
}

EvaluatorConditionSpec

Element of QualifiedRangeSpec.conditions. Validated against EvaluatorMetricsRegistry: an unknown metric raises, and the registered evaluator validates operation/value.

EvaluatorConditionSpec {
  metric:    str          # must resolve in EvaluatorMetricsRegistry
  operation: str
  value:     dict | str
}

Resource specs (API schema)

All specs subclass BaseObservationDefinitionSpec (itself a subclass of EMRResource, with __model__ = ObservationDefinition and __exclude__ = ["facility"]). EMRResource provides serialize/de_serialize plus the perform_extra_serialization / perform_extra_deserialization hooks.

Spec class Role Notable fields / behaviour
BaseObservationDefinitionSpec Shared base id, title, status, description, category, code, permitted_data_type, component, body_site, method, permitted_unit, derived_from_uri, qualified_ranges. Runs validate_data_type and validate_qualified_ranges_consistency
ObservationDefinitionCreateSpec Write · create Adds facility: UUID4 | null and slug_value: SlugType. Validates the facility exists; perform_extra_deserialization resolves the facility and sets obj.slug = slug_value
ObservationDefinitionUpdateSpec Write · update Adds slug_value: SlugType; perform_extra_deserialization sets obj.slug = slug_value. The facility is not re-bound on update
ObservationDefinitionReadSpec Read · list & detail Adds version: int | null, facility: dict | null, slug_config: dict, slug: str. perform_extra_serialization sets id = external_id, serializes facility via FacilityBareMinimumSpec, and sets slug_config = parse_slug(slug)

Notes:

Methods & save behaviour

ObservationDefinition overrides nothing of its own. Slug handling comes from SlugBaseModel, and the create/update side effects live in the API specs.

Slug behaviour

SlugBaseModel sets FACILITY_SCOPED = True, so the stored slug is namespaced:

facility-scoped: f-<facility.external_id>-<slug_value>
instance-scoped: i-<slug_value>

The create/update specs receive the bare slug_value and store it on obj.slug; the model's slug helpers apply the namespacing.

API integration notes

Related

Source evidence

References are pinned to the private MANSOQ source repository. Repository access is required to open these links.