← Knowledge library

Clinical care

Notes

Notes are threaded discussions attached to a patient. A NoteThread groups messages; a NoteMessage is one message in that thread, carrying a server-maintained edit history. The Django models store the data: NoteThread holds the patient and…

3.1 · versioned documentation · 5 min read

Notes are threaded discussions attached to a patient. A NoteThread groups messages; a NoteMessage is one message in that thread, carrying a server-maintained edit history.

The Django models (care/emr/models/notes.py) store the data: NoteThread holds the patient and optional encounter link, and NoteMessage stores each body next to an opaque message_history JSONField. The Pydantic resource specs (care/emr/resources/notes/) extend EMRResource and define what the API exposes — the shape of message_history, the read/write schema split, and the edit-history logic that runs on every update.

Source:

Models

Model Purpose
NoteThread A discussion thread attached to a patient (optionally scoped to an encounter)
NoteMessage An individual message posted within a NoteThread

Both extend EMRBaseModel, which supplies external_id, created_date/modified_date, soft-delete via deleted, created_by/updated_by, and the history/meta JSON fields.

NoteThread fields

Field Type Notes
title CharField(255) Nullable and blank-allowed in the column, but the spec requires it on write (title: str, max_length=255)
patient FK → Patient on_delete=CASCADE; deleting the patient deletes the thread. Excluded from spec serialization (__exclude__) and set from the URL/viewset context, never the request body
encounter FK → Encounter Nullable, blank-allowed, on_delete=CASCADE. Scopes the thread to one encounter. Excluded from spec serialization; on create it is resolved from a UUID in the request body (see specs below)

A thread with no encounter is patient-level; a thread with an encounter is pinned to that visit. A TODO in the source flags organization-based access restriction as planned but not yet implemented.

Related models

NoteMessage

One message inside a thread.

Field Type Notes
thread FK → NoteThread on_delete=CASCADE; deleting the thread deletes its messages. Excluded from spec serialization (__exclude__) and set from the URL/viewset context
message TextField Free-text body. Required on write (message: str)
message_history JSONField Defaults to {}. Opaque at the model layer; the spec gives it the shape below and rewrites it server-side on each edit. Read-only on the API

message_history shape

The update spec writes a fixed structure, not a free-form blob. Each edit appends the previous message body to a history list:

{
  "history": [
    {
      "message": "string",           // the prior message body (pre-edit)
      "created_by": {
        "username": "string",
        "external_id": "uuid"        // editor of the prior version
      },
      "edited_at": "datetime str",   // timezone.now() at the time of this edit
      "created_at": "datetime str"   // the prior version's modified_date
    }
    // ... one entry per edit, oldest first
  ]
}

Clients never write message_history. It is read-only on the API and rebuilt server-side on each edit (see NoteMessageUpdateSpec below).

Resource specs (API schema)

Every spec extends EMRResource (resources/base.py), which provides serialize (DB object → pydantic, via perform_extra_serialization) and de_serialize (pydantic → DB object, via perform_extra_deserialization). Fields named in __exclude__ are skipped in both directions and handled by hand.

Thread specs

Spec Role Fields exposed Notes
NoteThreadSpec shared base id (UUID, read), title (str, required, ≤255) __model__ = NoteThread; __exclude__ = ["patient", "encounter"]
NoteThreadCreateSpec write · create base + encounter (UUID, optional) Validates encounter exists (raises Encounter not found), then resolves the UUID to an Encounter FK in perform_extra_deserialization
NoteThreadUpdateSpec write · update base (title) No extra behaviour; encounter and patient cannot be reassigned through update
NoteThreadReadSpec read · detail/list base + created_by, updated_by (dict, nullable), created_date, modified_date (datetime) perform_extra_serialization sets id = external_id and serializes audit users via serialize_audit_users

Validation and server behaviour:

Message specs

Spec Role Fields exposed Notes
NoteMessageSpec shared base id (UUID, read), message (str, required) __model__ = NoteMessage; __exclude__ = ["thread"]
NoteMessageCreateSpec write · create base (message) No extra behaviour
NoteMessageUpdateSpec write · update base (message) perform_extra_deserialization appends the pre-edit message to message_history["history"] (see below)
NoteMessageReadSpec read · detail/list base + message_history (dict), created_by, updated_by (dict, nullable), created_date, modified_date (datetime) perform_extra_serialization sets id = external_id and serializes audit users

Validation and server behaviour:

Methods & save behaviour

API integration notes

Payload field names follow the spec classes above, not the Django model names.

Related

Source evidence

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