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

References/Tagging: Difference between revisions

From OHC Network Wiki
Content deleted Content added
OHC identity seed
 
Add Navbox platform (via update-page on MediaWiki MCP Server)
 
Line 258: Line 258:


<code>metadata</code> carries display hints (<code>color</code>, <code>icon</code>) without a schema migration, shaped by <code>TagConfigMetadata</code>.
<code>metadata</code> carries display hints (<code>color</code>, <code>icon</code>) without a schema migration, shaped by <code>TagConfigMetadata</code>.

{{Navbox platform}}


{{Related}}
{{Related}}

Latest revision as of 09:55, 6 July 2026

referenceplatformCARE 3.0+

A TagConfig defines one tag: its label, category, scope, and place in a tag tree. Tags are hierarchical labels you attach to resources — patients, encounters, charge items — to classify and filter them. You manage the definitions through TagConfig; the applied tags live as integer ID arrays on the tagged resources.

Two layers back the tag. The Django model (care/emr/models/tag_config.py) is storage — it holds opaque JSONFields (metadata, cached_parent_json) whose structure it never describes. The Pydantic resource specs (care/emr/resources/tag/config_spec.py) are the API and implementation layer: they pin down the enums, give those JSON fields a real shape, run field validation, and split read from write.

Source:

Models

Model Purpose
TagConfig Definition of a single tag — its display, category, scope, status, metadata, and position in the tag tree

TagConfig extends EMRBaseModel, the shared Care EMR base providing external_id, the audit fields created_by/updated_by and created_date/modified_date, soft-delete via deleted, and history/meta JSON. Applied tag IDs live on the tagged resource itself — for example Patient.instance_tags / Patient.facility_tags and Encounter.tags.

TagConfig fields

Scope

A tag is scoped to at most one owner — an instance-wide organization, a facility-scoped organization, or a facility.

Field Type Notes
facility FK → Facility PROTECT, nullable, default=None. Facility that owns the tag
facility_organization FK → FacilityOrganization CASCADE, nullable. Owning facility organization. Not allowed on instance-level (no-facility) tags
organization FK → Organization CASCADE, nullable. Owning instance-wide organization

{{Field|model=Tagging|section=Scope|name=resource|type=CharField(255)|notes=The resource type the tag applies to. Constrained by TagResource in the write spec (see [[#tagresource-values}}

Display & classification

Field Type Notes

{{Field|model=Tagging|section=Display & classification|name=status|type=CharField(255)|notes=Lifecycle status. Constrained by TagStatus in the spec — active / archived (see [[#tagstatus-values}}

display CharField(255) Human-readable label. Required in the spec
description TextField Nullable, blank-able. Required key in the base spec but accepts null

{{Field|model=Tagging|section=Display & classification|name=category|type=CharField(255)|notes=Grouping category. Constrained by TagCategoryChoices in the spec (see [[#tagcategorychoices-values}}

priority IntegerField default=100. Ordering weight

{{Field|model=Tagging|section=Display & classification|name=metadata|type=JSONField|notes=default=None, nullable. Shape defined by TagConfigMetadata: { color: str?, icon: str? } (see [[#tagconfigmetadata-shape-of-metadata}}

Tree structure & caches

TagConfig is self-referential — tags form a tree, and denormalized caches are rebuilt on insert to avoid recursive joins (the same pattern as Organization). These fields are platform-maintained and never writable through the create/update specs.

Field Type Notes
parent FK → self CASCADE, nullable. related_name="children". Null parent means a root tag
root_tag_config FK → self CASCADE, nullable. related_name="root". Top of the tree, derived on save
has_children BooleanField default=False. Flipped to True on the parent when a child is created
level_cache IntegerField default=0. Depth in the tree (parent.level_cache + 1)
parent_cache ArrayField[int] default=list. Full ancestor id chain (parent.parent_cache + [parent.id])
cached_parent_json JSONField default=dict. Materialized parent record with a cache_expiry; rebuilt after cache_expiry_days (15). Shape: { id, display, description, category, parent (nested same shape), level_cache, cache_expiry }

Enums

TagCategoryChoices values

category binds to this enum in every spec (str values).

Value
diet
drug
lab
admin
contact
clinical
behavioral
research
advance_directive
safety

TagResource values

resource binds to this enum in TagConfigWriteSpec (set only on create; the read specs surface it as a plain str).

Value
encounter
activity_definition
service_request
charge_item
charge_item_definition
patient
token_booking
medication_request_prescription
supply_request_order
supply_delivery_order
account

TagStatus values

status binds to this enum in every spec (str values).

Value Meaning
active Tag is available for use
archived Tag retired; retained for historical references

Nested specs (shape of JSON fields)

TagConfigMetadata (shape of metadata)

Gives the opaque metadata JSONField a real structure. A plain Pydantic BaseModel, not an EMRResource.

Field Type Required Default Notes
color str no
icon str no

Resource specs (API schema)

Every spec extends EMRResource (care/emr/resources/base.py), which supplies serialize (DB object → spec) and de_serialize (spec → DB object) plus the perform_extra_serialization / perform_extra_deserialization hooks. __exclude__ on the base spec keeps the FK fields (facility, facility_organization, organization, parent) out of the generic field copy so each spec can resolve them explicitly.

Spec class Role Notes
TagConfigBaseSpec shared None), priority (int = 100), status (TagStatus), metadata (TagConfigMetadata?)
TagConfigWriteSpec write · create Base + facility?, facility_organization?, organization?, parent? (all UUID4), resource (TagResource, required). Runs two model_validator(after) checks and resolves FKs in perform_extra_deserialization
TagConfigUpdateSpec write · update Base + facility_organization?, organization? (UUID4). Cannot change facility, parent, or resource. Resolves organization/facility_organization in perform_extra_deserialization (facility organization scoped to obj.facility)
TagConfigReadSpec read · list None), resource (str), facility (dict | None). Inlines parent via get_parent_json() and a FacilityBareMinimumSpec for facility
TagConfigRetrieveSpec read · detail None), organization (dict | None). Adds audit users and full org/facility-org serialization

Write-spec validation (TagConfigWriteSpec)

Two @model_validator(mode="after") checks run before deserialization:

  • validate_exists:
    • If facility is set, it must exist; if facility_organization is also set it must belong to that facility, else ValidationError("Facility Organization not found").
    • If organization is set, it must exist, else ValidationError("Organization not found").
    • If parent is set, a TagConfig with that external_id and the same resource must exist (and, when facility is set, belong to that facility), else ValueError("Parent tag config not found").
  • validate_organizations: a facility_organization without a facility is rejected — ValueError("Facility Organization not allowed in instance level tag configs").

Server-maintained behaviour

  • TagConfigWriteSpec.perform_extra_deserialization resolves parent, organization, and facility (plus facility_organization scoped to the resolved facility) from their external_ids onto the model instance; parent is explicitly set to None when absent.
  • TagConfigReadSpec.perform_extra_serialization sets id = external_id, inlines the parent record via obj.get_parent_json() (only when non-empty), and serializes facility with FacilityBareMinimumSpec ({ id, name }).
  • TagConfigRetrieveSpec.perform_extra_serialization calls the read-spec hook, then serialize_audit_users (populates created_by/updated_by from cache) and serializes facility_organization / organization with their full read specs.
  • Tree caches (level_cache, parent_cache, root_tag_config, has_children) are populated by the model's set_tag_config_cache() on insert — never accepted from clients.
  • system_generated is declared on TagConfigReadSpec but is not a field on the current TagConfig model (nor on EMRBaseModel). The generic serialize only copies model fields, so it is not populated from storage unless something else provides it. Treat it as a read-only flag and verify against the live model before relying on it.

Cache invalidation

A post_save signal connects invalidate_tag_config_cache to TagConfig. Every save does three things.

  1. Invalidates the tag's own TagConfigReadSpec cache and resets cached_parent_json = {}.
  2. Invalidates all descendants (rows whose parent_cache overlaps the tag id) — they embed parent data via get_parent_json().
  3. Invalidates the parent's cache (its has_children may have changed).

Methods & save behaviour

set_tag_config_cache()

When a parent is set, recomputes parent_cache (parent.parent_cache + [parent.id]) and level_cache (parent.level_cache + 1), then derives root_tag_config from the parent — the parent itself if the parent is a root, otherwise the parent's root_tag_config. If the parent had no children, it flips parent.has_children = True, persisting only that field. Finishes with super().save().

get_parent_json()

Returns cached_parent_json when it is present and its cache_expiry is still in the future. Otherwise it recursively rebuilds the nested parent record (id = parent external_id, display, description, category, nested parent, level_cache), stamps a fresh expiry (cache_expiry_days = 15), persists cached_parent_json, and returns it. Returns {} for root tags.

save() side effects

On insert (no id yet), the base save() runs first, then set_tag_config_cache() populates the tree caches in a second write pass. On update, save() persists normally without recomputing caches. Both paths trigger the post_save cache-invalidation signal described above.

API integration notes

The REST API exposes only the definitions. Applied tags are stored as integer ID arrays on each tagged resource (e.g. Patient.instance_tags/facility_tags, Encounter.tags), not as join rows.

Each operation maps to one spec:

  • Create — TagConfigWriteSpec. Sets resource, the scope FKs, and parent. resource must match a TagResource value.
  • Update — TagConfigUpdateSpec. Reassigns org/facility-org only, plus the shared display/category/status/metadata fields; facility, parent, and resource are fixed.
  • List — TagConfigReadSpec. Detail — TagConfigRetrieveSpec.

metadata carries display hints (color, icon) without a schema migration, shaped by TagConfigMetadata.

Pages that link here: