Custom subject types¶
Who this is for: the integration developer or project author whose DHIS2 tracker programs follow things that are not people - herds, water points, vehicles, lab samples - and who needs the published FHIR to say so.
Before you start: The capture contract, for where a tracker response's subject lives and how it is identified.
You will be able to:
- map a DHIS2 tracked entity type onto the FHIR resource type it really is
- predict every artifact that one mapping changes
- read the union rule the two tracker response profiles publish under
- know what a running facade checks about a subject's type, and when
What a tracked entity type is¶
DHIS2 tracks people most of the time, and buildings, herds, water points,
vehicles, and lab samples the rest of the time - all through the same
registration-and-stages shape. The tracked entity type is the DHIS2 object
that owns the nature of the thing being tracked; every tracker program
declares one. FHIR, by contrast, says what a thing is through its resource
type - and a QuestionnaireResponse about a vaccination visit for a
livestock herd whose subject claims to be a Patient is simply wrong.
[generate.tracked_entity_types] in fhir.toml is where the two meet: it
maps a tracked entity type UID onto the FHIR resource type its registrations
are about.
[generate.tracked_entity_types]
"Kd6Nk9wnAJa" = "Group" # a livestock herd tracked through a vaccination programme
"Bx8L1nQ4EiP" = "Location" # a water point tracked through a maintenance programme
The key is the tracked entity type, not the program: the type owns the
nature of the thing, so two programs tracking the same type agree by
construction and neither can be configured into disagreeing with the other.
Selecting the programs is still [generate.tracker_programs]' job - naming
a type here selects nothing.
A type this table never mentions is a Patient. A project that tracks
people configures nothing here, and everything it generates is what it would
have generated without the table at all.
On an instance with many types that default is worth checking rather than
assuming, so d2w fhir validate prints the list: one row per tracked entity type
the instance holds that this table does not name, carrying the UID, the name the
instance holds, and the line that would type it. A fifty-type instance gets a
fifty-row checklist instead of silence.
One entry feeds every artifact that has to say so¶
Questionnaire.subjectTypeon the program's registration form and on every stage form of that program - a stage captures a visit by the very entity the registration enrolled, so the two cannot disagree.subject.typeon the generated example responses, and on whatever$generatemints when the compiled form is served.- The reference targets the two tracker response profiles admit (next section).
The admitted types¶
The value must be one of a deliberate subset of R4's resource types - the
types a longitudinal DHIS2 record is plausibly kept about, declared as
SUBJECT_RESOURCE_TYPES in dhis2w_fhir.r4:
| Resource type | A tracked entity it fits |
|---|---|
Patient |
the default - a person in care |
Person |
a person outside a care relationship |
Practitioner |
a health worker |
RelatedPerson |
a caregiver or family member |
Group |
a herd, a household, a cohort |
Device |
a vehicle, a fridge, equipment |
Location |
a water point, a building |
Organization |
a facility as an administrative thing |
Specimen |
a lab sample |
Anything else is refused when fhir.toml is read, naming the entry and
listing what it could have been - a typo here would mis-type every form of
every program tracking that type:
tracked entity type Kd6Nk9wnAJa is mapped to 'Herd', which is not a FHIR resource
type a tracked entity is published as: name one of Patient, Person, Practitioner,
RelatedPerson, Group, Device, Location, Organization, Specimen
For comparison, the non-tracker kinds need no table: an aggregate or event
form is reported for a place, so FORM_KIND_PROFILES pins their
subjectType to Location outright, and only the two tracker kinds default
to Patient and read this mapping.
The union rule the profiles publish¶
A response profile is published once for the whole IG, so it cannot pin the
subject type of one program. D2TrackerRegistrationResponse and
D2TrackerEventResponse therefore admit the union of Patient and
every type the table names:
- a project that maps nothing publishes
subject only Reference(Patient), exactly as it would have without the table; - the herd-and-people project above publishes
subject only Reference(Patient or Group or Location).
Patient is always in the union - a tracked entity type the project maps to
nothing is a Patient - and the published order is the declaration order of
SUBJECT_RESOURCE_TYPES, not the order entries were written in fhir.toml,
so regenerating an unchanged project publishes unchanged bytes.
Which type a given form's responses actually carry is pinned by that form's
own subjectType; the union on the profile is only as wide as the project's
configuration made it. Whatever the type, the subject is identified the same
way - subject.identifier under {base}/id/tracked-entity, carrying the
DHIS2 UID - because the guide publishes no instances of any of these types.
A herd is as logical a subject as a person.
Two types, one resource¶
Nothing says a project maps two types onto different resources. A cold-chain
fridge and a delivery vehicle are both a Device; two sample types are both a
Specimen. What that becomes when the guide is served is one rule:
One FHIR resource type is one register serving the union of its tracked entity
types. GET /Device searches, lists, and counts the fridges and the vehicles
together, in the order the guide registers them. Nothing collides, nothing is
refused, and neither type overwrites the other - the published map is read into
one row per type, and the register groups the rows by resource.
Each resource still says which DHIS2 type it is. meta.tag carries the tracked
entity type UID under {identifier_system_base}/id/tracked-entity-type, which
is R4's own element for classifying a resource, so a page holding both kinds is
a page where every row states which kind it is. /metadata states it ahead of
the request too: the register's entry names every tracked entity type behind it.
To ask that register about one of its types, search _tag - R4's token search
over exactly the element the type is stated in:
GET /Device?_tag=http://dhis2.org/fhir/id/tracked-entity-type|TetFridge01
GET /Device?_tag=TetFridge01
It narrows the listing, the identifier search, and the _count=0 count alike,
and it rides the listing's own next and previous links so a walk stays inside
the type it started in. See
The register for the paging detail
and the fifty-type checklist.
The capture side needs no rule of its own: two types mapped to one resource are
two forms declaring the same subjectType, and each form still carries its own
tracked entity type identifier - so $generate types both subjects Device
while d2w fhir forward files each registration under the DHIS2 type its form
names.
What a running facade checks¶
The server reads the declared type off the compiled Questionnaire and
nowhere else - fhir.toml is the generator's input and never reaches a
running facade. A response typing its subject as something else - a
Patient sent to a form asking for a Group - is a warning by default and
a 422 under --strict-codes, the same dial that grades coded answers
(Serve the guide).
Reference.type is optional in R4 and the profile asks for the subject by
identifier, so a response that carries no type at all is complete, and
nothing is checked.
Next: Regeneration and hand-authoring - which files a re-run owns and where hand-written content lives. The capture contract covers the rest of what a tracker response carries.