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

References/Notes

From OHC Network Wiki
Revision as of 04:14, 5 July 2026 by Admin (talk | contribs) (OHC identity seed)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
referenceclinicalFHIR: NotesCARE 3.0+

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)

{{Field|model=Notes|section=NoteThread fields|name=patient|type=FK → Patient|notes=on_delete=CASCADE; deleting the [[References/Patient}} {{Field|model=Notes|section=NoteThread fields|name=encounter|type=FK → Encounter|notes=Nullable, blank-allowed, on_delete=CASCADE. Scopes the thread to one [[References/Encounter}}

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.

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:

  • title is required on write at the spec layer (Field(..., max_length=255)), even though the column is nullable.
  • NoteThreadCreateSpec.encounter runs validate_encounter_exists: a non-null value must match an existing Encounter.external_id, or it raises ValueError("Encounter not found").
  • On create, perform_extra_deserialization looks up the Encounter by external_id and assigns the FK. patient belongs to no thread spec — it comes from the request context.

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:

  • message is required on create and update.
  • thread is never in the request body (__exclude__); it comes from the URL/viewset context.
  • On update, NoteMessageUpdateSpec.perform_extra_deserialization fetches the existing NoteMessage by external_id, initializes message_history["history"] = [] if empty, then appends an entry: the prior message, the prior author (username + external_id), edited_at (timezone.now()), and created_at (the prior modified_date). Clients cannot write message_history directly.

Methods & save behaviour

  • NoteThread and NoteMessage inherit save and audit behaviour from EMRBaseModel: external_id, created_by/updated_by, created_date/modified_date, and soft delete via deleted.
  • Read specs populate audit users through EMRResource.serialize_audit_users, which resolves created_by/updated_by to cached UserSpec dicts.
  • message_history changes only inside NoteMessageUpdateSpec.perform_extra_deserialization — never from client input.
  • Cascade deletes flow downward: deleting a patient removes its threads, and deleting a thread removes its messages.

API integration notes

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

  • patient and thread never appear in a request body; they come from the URL/viewset context.
  • Send encounter on create as the encounter's external_id (UUID); it is validated to exist. It cannot be reassigned on update.
  • title and message are required on write, even though the underlying columns are nullable.
  • message_history is read-only. Each edit appends the previous body and authorship to history server-side.
  • created_by, updated_by, created_date, and modified_date are read-only audit fields from EMRBaseModel; soft deletes use deleted.

Pages that link here: