An ActivityDefinition is a FHIR-aligned, facility-scoped template for a clinical activity. It stores the defaults needed to spin up a downstream resource — currently a Service Request — and you "apply" it against an encounter, which merges those defaults with the encounter context to produce the live resource.
Source:
The Django model is just storage. Several fields are opaque JSONFields or integer ArrayFields whose real structure lives in the Pydantic resource specs — the API layer that defines the enums, JSON shapes, validation rules, and the split between read and write schemas. Neither layer is complete on its own; read them together.
Models
| Model | Purpose |
|---|
ActivityDefinition | Versioned, facility-scoped template describing a clinical activity and the defaults used to create the resources it produces |
ActivityDefinition extends SlugBaseModel, which adds a facility-scoped slug 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. See Base model.
ActivityDefinition fields
Each field lists its storage type (Django model) and, where the spec constrains it, the API shape or values the Pydantic specs enforce.
Identity & versioning
| Field | Storage type | API shape / notes |
|---|
facility | FK → facility.Facility (PROTECT) | Owning facility. Spec-__exclude__d — set server-side and never accepted in the payload. |
slug | CharField(255) | Facility-scoped identifier. On write you supply it as slug_value (SlugType); perform_extra_deserialization assigns it to obj.slug. On read it comes back as slug plus a parsed slug_config dict. |
version | IntegerField | Default 1. Read-only in specs (version: int | None), serialized on list and retrieve. Drives the append-only version chain. |
latest | BooleanField | Default True. Marks the current version within a slug's chain. Server-maintained, not exposed in the specs. |
derived_from_uri | TextField | Nullable. URI this definition was derived from. Optional in specs (str | None = None). |
| Field | Storage type | API shape / notes |
|---|
title | CharField(1024) | Required (str). Human-friendly name. |
status | CharField(255) | Required enum — ActivityDefinitionStatusOptions (draft / active / retired / unknown). |
classification | CharField(100) | Required enum — ActivityDefinitionCategoryOptions (laboratory / imaging / counselling / surgical_procedure / education). |
kind | CharField(100) | Required enum — ActivityDefinitionKindOptions (only service_request). Determines the resource produced on apply. |
description | TextField | Optional in specs; default "". |
usage | TextField | Optional in specs; default "". |
Coded clinical attributes (JSON)
These are JSONFields. The spec binds each to a value set via ValueSetBoundCoding[<slug>], which validates the submitted code against that value set. Every coding is shaped { system?: str, version?: str, code: str (required), display?: str } with extra="forbid", so unknown keys are rejected.
| Field | Storage type | API shape / notes |
|---|
code | JSONField (nullable) | Required Coding bound to value set activity-definition-procedure-code (SNOMED CT, concept is-a 71388002 "Procedure"). Nullable in the DB but rejected on write if absent. |
body_site | JSONField (nullable) | Optional Coding | None = None bound to value set system-body-site-observation (SNOMED CT body sites). |
diagnostic_report_codes | JSONField (nullable) | list[Coding], default []. Each Coding bound to value set system-observation (LOINC). |
Requirement & linkage arrays
These store integer primary keys, not Django FK relations — ArrayField(IntegerField, default=list). The specs never expose those raw PKs: writes reference targets by external slug or UUID, and retrieves expand them back into serialized objects.
| Field | Storage type | Write shape | Read (retrieve) shape |
|---|
specimen_requirements | ArrayField[int] | list[ExtendedSlugType] | list[dict] — serialized Specimen Definitions (SpecimenDefinitionReadSpec) |
observation_result_requirements | ArrayField[int] | list[ExtendedSlugType] | list[dict] — serialized Observation Definitions (ObservationDefinitionReadSpec) |
locations | ArrayField[int] | list[UUID4] | list[dict] — serialized Locations (FacilityLocationListSpec) |
charge_item_definitions | ArrayField[int] | list[ExtendedSlugType] | list[dict] — serialized Charge Item Definitions (ChargeItemDefinitionReadSpec) |
tags | ArrayField[int] | not written via spec | list[dict] — rendered via SingleFacilityTagManager().render_tags(obj) |
ExtendedSlugType: 7–88 chars, URL-safe, must start with f- or i-. SlugType (used for slug_value): 5–50 chars, URL-safe. The stored integer arrays carry no referential integrity — nothing stops you storing an ID whose target is later deleted, and retrieve serialization silently skips any ID that no longer resolves.
Service relationships
| Field | Storage type | API shape / notes |
|---|
healthcare_service | FK → emr.HealthcareService (PROTECT) | Nullable; default None. Write: UUID4 | None, resolved by external_id in perform_extra_deserialization. Retrieve: serialized via HealthcareServiceReadSpec. See Healthcare Service. |
category | FK → emr.ResourceCategory (CASCADE) | Nullable. Write: ExtendedSlugType | None, resolved by slug. Read: serialized via ResourceCategoryReadSpec. Deleting the Resource Category cascades to the definition. |
Enum values
ActivityDefinitionStatusOptions (status)
| Value |
|---|
draft |
active |
retired |
unknown |
ActivityDefinitionKindOptions (kind)
| Value | Notes |
|---|
service_request | Only supported kind; applying the definition produces a Service Request |
ActivityDefinitionCategoryOptions (classification)
| Value |
|---|
laboratory |
imaging |
counselling |
surgical_procedure |
education |
Bound value sets
| Field | Value set slug | Source system / filter |
|---|
code | activity-definition-procedure-code | SNOMED CT, concept is-a 71388002 (Procedure) |
body_site | system-body-site-observation | SNOMED CT body-site concepts |
diagnostic_report_codes[] | system-observation | LOINC (http://loinc.org) |
Resource specs (API schema)
All specs extend EMRResource (see Base model), which provides serialize (DB → Pydantic) and de_serialize (Pydantic → DB) plus the perform_extra_serialization / perform_extra_deserialization hooks. Here __model__ = ActivityDefinition and __exclude__ = ["facility"].
| Spec class | Role | Notes |
|---|
BaseActivityDefinitionSpec | shared base | id, title, derived_from_uri, status, description, usage, classification, kind, code, body_site, diagnostic_report_codes. |
ActivityDefinitionWriteSpec | write · create + update | Adds locations (list[UUID4]), specimen_requirements / observation_result_requirements / charge_item_definitions (list[ExtendedSlugType]), healthcare_service (UUID4 | None), category (ExtendedSlugType | None), slug_value (SlugType). |
ActivityDefinitionReadSpec | read · list | Base fields plus version, tags, category (serialized dict), slug, slug_config. |
ActivityDefinitionRetrieveSpec | read · detail | Extends ReadSpec; expands specimen_requirements, observation_result_requirements, locations, healthcare_service, charge_item_definitions from ID arrays into fully serialized nested objects. |
Server-side behaviour
ActivityDefinitionWriteSpec.perform_extra_deserialization (write path):
- Resolves
healthcare_service by external_id, or clears it to None when absent.
- Resolves
category by slug, only when provided.
- Assigns
obj.slug = self.slug_value.
ActivityDefinitionReadSpec.perform_extra_serialization (read path): sets id = external_id, renders tags, serializes category (when present) via ResourceCategoryReadSpec, and parses slug_config = obj.parse_slug(obj.slug).
ActivityDefinitionRetrieveSpec.perform_extra_serialization: calls the parent, then resolves each ID array (specimen_requirements, observation_result_requirements, locations, charge_item_definitions) against its model — dropping IDs that no longer exist — and serializes healthcare_service.
- The base
de_serialize writes only model-mapped fields with exclude_defaults=True, so any unset optional field falls back to its model default.
Methods & save behaviour
- Apply → Service Request (
service_request.py): convert_ad_to_sr(activity_definition, encounter) builds a draft ServiceRequest. It carries over title, category, code, body_site, locations, and healthcare_service, and binds patient/encounter from the encounter. The new request starts at status=draft, intent=proposal, priority=routine, do_not_perform=False.
- Charge items on apply:
apply_ad_charge_definitions(activity_definition, encounter, service_request) loads every linked Charge Item Definition and, for each, instantiates a Charge Item (via apply_charge_item_definition), tags it to the service request (service_resource = service_request, service_resource_id = external_id), copies the creator, and sets performer_actor to the request's requester when one is set.
version and latest form an append-only chain per slug: query latest=True for the current definition; superseded versions stay readable so existing references don't break.
API integration notes
- Payload field names follow the Pydantic specs, not the Django model. Send
slug as slug_value. Reference specimen_requirements, observation_result_requirements, charge_item_definitions, and category by external slug (ExtendedSlugType, f-/i- prefixed); reference locations and healthcare_service by UUID.
code is required and must validate against the activity-definition-procedure-code value set. body_site and each entry of diagnostic_report_codes validate against their bound value sets.
- Apply instantiates the target
kind (only service_request today) by merging stored defaults with the encounter, and instantiates any linked charge_item_definitions in the same pass.
- The requirement and linkage fields are plain integer ID arrays. Retrieve serialization resolves them to nested objects and skips any ID that no longer resolves, so a response can omit a requirement you stored.