Toggle menu
Toggle preferences menu
Toggle personal menu
Not logged in
Your IP address will be publicly visible if you make any edits.

References/Observation Definition

From OHC Network Wiki
referencedefinitionsCARE 3.0+

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 [https://build.fhir.org/observationdefinition.html 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

{{Field|model=ObservationDefinition|section=Identity & scope|name=slug|type=CharField(255)|notes=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

{{Field|model=ObservationDefinition|section=Identity & scope|name=status|type=CharField(255)|notes=Required. Lifecycle code from ObservationStatusChoices — see [[#status-values}}

description TextField Required. Natural-language (markdown) description
derived_from_uri TextField External URI this definition was derived from. Optional (str

Coding & measurement

Field Type Notes

{{Field|model=ObservationDefinition|section=Coding & measurement|name=category|type=CharField(255)|notes=Nullable in the model, but required in the API as ObservationCategoryChoices — see [[#category-values}} {{Field|model=ObservationDefinition|section=Coding & measurement|name=code|type=JSONField|notes=Required. What is being observed: a Coding bound to the system-observation value set (LOINC) — see [[#coding-shape}} {{Field|model=ObservationDefinition|section=Coding & measurement|name=permitted_data_type|type=CharField(100)|notes=Required. QuestionType enum (questionnaire data types) — see [[#permitted-data-type-values}}

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). Care does not convert between units when displaying data

Components & ranges

Field Type Notes

{{Field|model=ObservationDefinition|section=Components & ranges|name=component|type=JSONField|notes=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}} {{Field|model=ObservationDefinition|section=Components & ranges|name=qualified_ranges|type=JSONField|notes=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:

  • Either ranges (numeric) or at least one coded value set / valueset_interpretation (categorical) — one is required.
  • Numeric ranges and coded value sets cannot both appear.
  • Numeric ranges must not overlap (sorted by min, treating missing min/max as -∞/+∞).
  • Coded value-set slugs (normal_coded_value_set, critical_coded_value_set, abnormal_coded_value_set, and each valueset_interpretation.valuset) must be unique — duplicates are rejected.

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 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 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:

  • Value-set bindingscode, body_site, method, permitted_unit (and component equivalents) are ValueSetBoundCoding[...]; the submitted coding is validated against the bound value set (system-observation, system-body-site-observation, system-collection-method, system-ucum-units).
  • Data-type guardpermitted_data_type (definition and component) rejects group, display, url.
  • Range consistencyvalidate_qualified_ranges_consistency requires every qualified_ranges entry on a definition to be the same kind: all numeric ranges or all coded value sets. Mixing raises.
  • slug_valueSlugType: 5–50 chars, URL-safe ([a-zA-Z0-9][a-zA-Z0-9_-]*[a-zA-Z0-9]), starting and ending alphanumeric. Stored namespaced as obj.slug (see below).
  • No metadata store__store_metadata__ is False, so spec fields map directly to model columns rather than into meta.

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>
  • calculate_slug() returns the f-… form when facility is set, otherwise the i-… form.
  • parse_slug(slug) validates the prefix and returns a dict: {"facility": <uuid>, "slug_value": <bare slug>} for f- slugs, or {"slug_value": <bare slug>} for i- slugs. ObservationDefinitionReadSpec exposes this as slug_config.

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

  • The facility binding is write-once: the create spec resolves the facility UUID to an FK; the update spec only re-sets slug_value and never re-binds the facility.
  • Retire definitions via status (retired) instead of deleting. The inherited deleted flag gives you ORM-level soft-delete if you need it.
  • permitted_unit codings follow UCUM, but Care never converts between units at display time — store data in the unit you intend to read.
  • convert_od_to_observation (observation.py) builds an Observation from a definition for a given encounter, carrying over category and code and defaulting status to final.