# Original Patient Lists API Based on Characteristic
[TOC]
## Background
User-facing apps often need to know things like:
- "who are the patients I'm seeing today,"
- "who are the patients I'm responsible for in the hospital right now,"
- "who are the patients in this ward."
This is core functionality supported by existing EHR systems. In FHIR, various methods have been used such as the standard search API or assembling the List or Group resource. However, no standard or guidance for creation of and manipulation of patient lists currently exists.
## Goals
* Support existing EHR functionality in a standard way
* Enable access to "user-facing lists", meaning lists of patients that are designed for a clinical end-user to interact with in the context of a healthcare workflow
- include both "system and user-defined lists"
* Define a general framework for creating and managing patient lists so that systems can support any list types they choose to define (including non-standardized list types)
* Define a few common *system-maintained* list types for key use cases:
1. All Patients by Provider
1. Patient Rounds List by Provider
1. Today Patients by Provider - based on timeframe ( today, tomorrow, etc)
1. All Patients by Location
1. All Patients by Service
1. All Patients by Location and Status (e.g. , In hospital, discharged)
1. [?Recently accessed]
* Provide API access to any *system-maintained* lists
* Provide API access to any *user-maintained* lists, which are entirely ad-hoc (i.e., the user explicitly selects and manages members).
* API would allows user-facing apps to:
* discover any "user-facing lists", and their parameters
* enumerate members of a *system-maintained* or *user-maintained* list by supplying parameters For example:
* "All patients being seen today by <<Practitioner X>>"
* "All patients currently in <<Location Y>>"
* [?optional] create and manage a by explicit membership
* Define how additional information may be associated with each patient on the list.
## API Design Sketch
### [Group](http://build.fhir.org/group.html) vs [List](http://build.fhir.org/list.html)
There is some overlapping functionality between List and Group in FHIR. The methodological difference is that:
> Group is an actor - a collection that can be a subject of an observation, a recipient of data, etc. It is primarily defined by criteria... List on the other hand, is NOT an actor. It is an organizer for presentation purposes only. It is always enumerated and has no defined criteria.
Whether this difference merits 2 distinct resources remains an open question. The key "practical" advantage of Groups is their support for both **defining characteristics** via the `Group.characteristic` element and ability to **enumerating** the members. This is a natural fit for using parameters to discover any “user-facing lists”. In other words, it allow querying for a patient list based on multiple parameters and returning them as a list. For example, for the following group:
```js
{
"resourceType": "Group",
"characteristic": [{
{
"code": {
"coding": [
{
"system": "http://hl7.org/fhir/resource-types",
"code": "Location",
"display": "Location"
}
],
"text": "Location"
},
"valueReference": {
"reference": "Location/123"}}
],
"member": [] // references to patients at location/123 enumerated here
}
```
could be retrieved by the following FHIR RESTful search.
`Get [Base]Group?characteristic-value=Location$Location/123`
:::info
Note that the standard search parameter `characteristic-value` is only defined for valueCodeableConcept and valueBoolean. It would need to be extended to allow for retrieving the Group resource based on characteristic reference values.
:::
See [below](#Examples) for other examples of proposed searching on Group for patients lists
For consistency, it'd be nice to have the same resource supporting user-maintained and system-maintained lists, so the Group resource is the best candidate.
### Discovering, managing, and enumerating _User-maintained Lists_
This is the easy part. It's just FHIR CRUD operations on a simple Group profile for specifying entries in a User-managed list.
GET Group?managingEntity=[Practitioner or PractitionerRole or CareTeam]
[? optional] POST Group
[? optional] PUT Group/[id]
[? optional] DELETE Group/[id]
#### Example
*Get Dr Smith's custom patient list(s)*
**Request**:
`GET http://test.fhir.org/r4/Group?managing-entity=Practitioner/dr-smith[&type=person]`
**Response body**:
~~~js
{'resourceType': 'Bundle',
'type': 'searchset',
'total': 1,
...
{
"resourceType": "Group",
"id": "dr-smith-custom-list",
"active": true,
"type": "person",
"actual": true,
"name": "dr-smith-custom-list",
"managingEntity": {
"reference": "Practitioner/smith",
"display": "Doctor Smith"
},
"member": ... //[members]
}
}
~~~
### Discovering and enumerating _System-managed Lists_
FHIR doesn't have a built-in mechanism to manage "Parameterized List Definitions" -- e.g. to say " if you supply a location, I can give you a list of patients." For _system-managed_ Lists in addition to the managingEntity searchparameter = Organization, another approach would be needed to filter down even further. We consider a few here...
#### Option 1: Propagate a Patient list for every parameter combination
One way to address this would be to create a `Group` resource for every possible set of parameter values, so if a health system has 94 locations and supports a "patients currently in location" List type, it would support `GET Group/[id]` for 94 distinct IDs, and each one would have a unique parameter (aka metadata) in `characteristic` to describe its function as shown above.
Pros:
* Works with standard FHIR R4 API
* Allow query for a patient list based on multiple parameters, by specifying repeated search params as shown above (`Group?characteristic-value=by-location$Location/123`)
Cons:
* Doesn't allow easy discovery of list *types* (e.g. to learn "this server supports Location-based Lists")
* The number of lists may grow combinatorially large, with multiple parameters (e.g., by location + acuity + practitioner) -- though a system would most likely only create them "on-demand"
We could address the first con by **simply leaving it out of scope**. Alternatively, we could try to address it with an explicit discovery operation like:
GET Group/$system-maintained-list-types
or
GET .well-known/system-maintained-types.json
or
GET /metadata
... whatever :)
which could return something like:
```
"systemMaintainedLists": [{
"title: "All patients by provider",
"expansionParameters": ["Reference(Practitioner|PractitionerRole|CareTeam)"]
},{
"title": "Day's patients by provider",
"expansionParameters": ["DateTime", "Reference(Practitioner|PractitionerRole|CareTeam)"]
},
...
}]
```
#### Option 2: Custom operations
Another way to address this use case is to provide explicit operation-based APIs. For example, the following operation could return a set of system-maintained list types, and parameters required for each:
```
Get $patient-list?location=123&practitioner=123
```
Pros:
* API tailored for this specific use case
Question:
* Is this functionally any different than Option 1 (i.e., server may behave the same)?
Cons:
* Doesn't allow easy discovery of list *types* (e.g. to learn "this server supports Location-based Lists") - see above
* Consistent support for operations can be challenging across servers
* Working with Parameters is cumbersome, especially if you want a consistent OperationDefinition across a variety of parameterized list types
### Examples based on Option 1
(source [excel spreadsheet](https://github.com/argonautproject/patient-lists/blob/master/my_patients.xlsx))
**1. All Patients**
*Get all patients for Acme Clinic*
**Request**:
`GET http://test.fhir.org/r4/Group?managing-entity=Organization%2Fexample&characteristic-value=all%26true`
:::info
Note this search fails on the Hapi Test Server :-( perhaps since the LOINC answer code is not recognized or the codes are not bound to a valueset
:::
**Response body**:
~~~js
{"resourceType": "Bundle",
"type": :"searchset",
"total": 1,
...
{
"resourceType": "Group",
"identifier": [
{
"system": "http://example.org",
"value": "group-2020-05-03T16:05:59.390962"
}
],
"active": true,
"type": "person",
"actual": true,
"name": "all_patients",
"managingEntity": {
"reference": "Organization/example",
"display": "example managingEntity"
},
"characteristic": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "LA18187-7",
"display": "All"
}
],
"text": "All"
},
"valueBoolean": true,
"exclude": false,
"period": {
"start": "2020-05-03",
"end": "2020-05-05"
}
}
],
"member": [
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "123"
},
"display": "Name = Amy Brown (MRN = 123)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "234"
},
"display": "Name = Bert Black (MRN = 234)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "345"
},
"display": "Name = Colleen Blue (MRN = 345)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "456"
},
"display": "Name = Daniel Crimson (MRN = 456)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "567"
},
"display": "Name = Elizabeth Green (MRN = 567)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "689"
},
"display": "Name = Frederick Gray (MRN = 689)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "890"
},
"display": "Name = Greg Orange (MRN = 890)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "901"
},
"display": "Name = Harriet Silver (MRN = 901)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "902"
},
"display": "Name = Imelda Gold (MRN = 902)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "903"
},
"display": "Name = Jack White (MRN = 903)"
},
"inactive": false
}
]
}
}
~~~
**2. All Patients by Provider**
*Get all patients for Dr Smith*
**Request**:
`GET http://hapi.fhir.org/baseR4/Group?managing-entity=example-organization-2&characteristic-value=Practitioner%26Practitioner%2Fsmith`
:::info
Note the custom search parameter "characteristic-referencevalue" is *proposed* for retrieving the Group resource based on characteristic reference values.
:::
**Response body**:
~~~js
{"resourceType": "Bundle",
"type": :"searchset",
"total": 1,
...
{
"resourceType": "Group",
"id": "group-practitioner-20200505054346930424",
"identifier": [
{
"system": "http://example.org",
"value": "group-practitioner-20200505054346930424"
}
],
"active": true,
"type": "person",
"actual": true,
"name": "practitioner",
"managingEntity": {
"reference": "Organization/example-organization-2",
"display": "US Core Example Organization 2"
},
"characteristic": [
{
"code": {
"coding": [
{
"system": "http://hl7.org/fhir/resource-types",
"code": "Practitioner",
"display": "Practitioner"
}
],
"text": "Practitioner"
},
"valueReference": {
"reference": "Practitioner/smith",
"display": "Doctor Smith"
},
"exclude": false,
"period": {
"start": "2020-05-03",
"end": "2020-05-05"
}
}
],
"member": [
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "123"
},
"display": "Name = Amy Brown (MRN = 123)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "345"
},
"display": "Name = Colleen Blue (MRN = 345)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "567"
},
"display": "Name = Elizabeth Green (MRN = 567)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "890"
},
"display": "Name = Greg Orange (MRN = 890)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "902"
},
"display": "Name = Imelda Gold (MRN = 902)"
},
"inactive": false
}
]
}
}
~~~
**3. Patient Rounds List by Provider (?? how would this be filtered)**
**4. Today's Patients by Provider - based on timeframe ( today, tomorrow, etc)**
*Get all patients for Dr Smith for today = 5/4/2020*
**Request**:
`GET http://hapi.fhir.org/baseR4/Group?managing-entity=example-organization-2&characteristic-value=Practitioner%26Practitioner%2Fsmith&timeframe=2020-05-04`
:::info
Note the custom search parameters "characteristic-referencevalue" and timeframe are *proposed* for retrieving the Group resource based on characteristic reference values and period.
:::
**Response body**:
~~~js
{"resourceType": "Bundle",
"type": :"searchset",
"total": 1,
...
{
"resourceType": "Group",
"id": "group-practitioner-today-20200505055520658789",
"identifier": [
{
"system": "http://example.org",
"value": "group-practitioner-today-20200505055520658789"
}
],
"active": true,
"type": "person",
"actual": true,
"name": "practitioner-today",
"managingEntity": {
"reference": "Organization/example-organization-2",
"display": "US Core Example Organization 2"
},
"characteristic": [
{
"code": {
"coding": [
{
"system": "http://hl7.org/fhir/resource-types",
"code": "Practitioner",
"display": "Practitioner"
}
],
"text": "Practitioner"
},
"valueReference": {
"reference": "Practitioner/smith",
"display": "Doctor Smith"
},
"exclude": false,
"period": {
"start": "2020-05-04",
"end": "2020-05-05"
}
}
],
"member": [
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "567"
},
"display": "Name = Elizabeth Green (MRN = 567)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "890"
},
"display": "Name = Greg Orange (MRN = 890)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "902"
},
"display": "Name = Imelda Gold (MRN = 902)"
},
"inactive": false
}
]
}
}
~~~
**4. All Patients by Location**
`...todo...` ( same as by practitioner above )
**5. All Patients by Service**
`...todo...` ( same as by practitioner above )
**6. All Patients by Location and Status (e.g. , In hospital, discharged)**
*Get all active patients for today = 5/4/2020*
**Request**:
`GET http://hapi.fhir.org/baseR4/Group?managing-entity=example-organization-2&characteristic-value=LA16666-2%26true&timeframe=2020-05-04`
:::info
Note the custom search parameter timeframe is *proposed* for retrieving the Group resource based on characteristic period.
:::
**Response body**:
~~~js
{"resourceType": "Bundle",
"type": :"searchset",
"total": 1,
...
{
"resourceType": "Group",
"id": "group-active-today-20200505060432348158",
"identifier": [
{
"system": "http://example.org",
"value": "group-active-today-20200505060432348158"
}
],
"active": true,
"type": "person",
"actual": true,
"name": "active-today",
"managingEntity": {
"reference": "Organization/example-organization-2",
"display": "US Core Example Organization 2"
},
"characteristic": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "LA16666-2",
"display": "Active"
}
],
"text": "Active"
},
"valueBoolean": true,
"exclude": false,
"period": {
"start": "2020-05-04",
"end": "2020-05-05"
}
}
],
"member": [
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "456"
},
"display": "Name = Daniel Crimson (MRN = 456)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "567"
},
"display": "Name = Elizabeth Green (MRN = 567)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "689"
},
"display": "Name = Frederick Gray (MRN = 689)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "890"
},
"display": "Name = Greg Orange (MRN = 890)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "901"
},
"display": "Name = Harriet Silver (MRN = 901)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "902"
},
"display": "Name = Imelda Gold (MRN = 902)"
},
"inactive": false
},
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "903"
},
"display": "Name = Jack White (MRN = 903)"
},
"inactive": false
}
]
}
}
~~~
**7. [?Recently accessed]**
## What about the rest of the columns beside patient name and id ?...
End users often sort through a list of patient to stratify them on some information about those patient. name, age, sex, when last viewed, todo, orders, some score, etc.
Can we leverage existing FHIR Models and APIs to provide the additional information in addition to patients that offers consistency, availability and scalability?
- Is this a must have or optional scope? (Current approach is to leave to to the client to fetch and display data using RESTful data query on USCore)
- How *structured* or *unstructured* would this data need to be (e.g., csv <--> FHIR Resource or resources for each member)?
- i.e. are columns interoperably defined?
- If yes, a column template/class, for example using a Questionnaire is required.
- If no, the cell values are automagically mapped to the appropriate column and we only need the raw data for the cell, which could be represented as a reference to another resource or even a mere string with a code
Options:
1. Current approach is to leave to the client to fetch and display data using RESTful data query on USCore
- Get Patient List then for each member Get X resources
- This approach is likely prohibitively expensive.
- GraphQL?
1. Creation of list types as defined above and define the other columns using an existing FHIR artifact:
- e.g. for [FHIR Questionnaire](http://build.fhir.org/questionnaire.html):
- discovery of a form definition that is bound to certain types of lists and defines each column.
- retrieval of List along with the associated member metadata (structured or unstructured) as associated QuestionnaireResponse for each member.
~~~
e.g.
An extension on Group that references Questionniare
then you can issue a queries for the column data,
(maybe with a custom operation) i.e. let client issue
batch queries for the columns associated row data in the
form of FHIR QA
or a single transaction to get a
Bundle with Group and QA entries or (contained QAs),
one for each member. and references via an extension on
each member.
or a single transaction to get a Group with
extensions on each member supplying both a reference
to a resource and the raw column data.
~~~
**8. All Patients by Location with column data
*Get all active patients for today = 5/4/2020*
**Request**:
`GET http://hapi.fhir.org/baseR4/Group?managing-entity=example-organization-2&characteristic-value=LA16666-2%26true`
**Response body**:
~~~js
{"resourceType": "Bundle",
"type": :"searchset",
"total": 1,
...
{
"resourceType": "Group",
"id": "group-active-today-20200505060432348158",
"extension": [
{
"url": "http://registry.fhir.org/argonaut/patientlistsquestionnairev1",
"valueReference": "http://hapi.fhir.org/baseR4/Questionnaire/1169270"
}],
"identifier": [
{
"system": "http://example.org",
"value": "group-active-today-20200505060432348158"
}
],
"active": true,
"type": "person",
"actual": true,
"name": "active-today",
"managingEntity": {
"reference": "Organization/example-organization-2",
"display": "US Core Example Organization 2"
},
"characteristic": [
{
"code": {
"coding": [
{
"system": "http://loinc.org",
"code": "LA16666-2",
"display": "Active"
}
],
"text": "Active"
},
"valueBoolean": true,
"exclude": false,
"period": {
"start": "2020-05-04",
"end": "2020-05-05"
}
}
],
"member": [
{
"entity": {
"identifier": {
"system": "http://example.org",
"value": "456"
},
"display": "Name = Daniel Crimson (MRN = 456)"
"extension": [
{
"url": "http://registry.fhir.org/argonaut/patientlistsquestionnaireresponsev1",
"valueReference": "http://hapi.fhir.org/baseR4/QuestionnaireResponse/1167262"
}],
},
"inactive": false
}
]
}
}
~~~
Alternatively, instead of the Group resource explicitly linking to individual QuestionnaireResponses, the client simply queries for all responses:
~~~
http://hapi.fhir.org/baseR4/QuestionnaireResponse?questionnaire=1169270
~~~
## Definition of Success
By the end of 2020, Argonaut publishes an Patient LIst guide that enables an authorized SMART on FHIR client application to access patient lists for the defined use cases
- IG implemented in an open-source sandbox + sample data + demo app
- IG prototyped by at least two server vendors, tested in September connectathon
- IG prototyped by at least two client vendors, tested in September connectathon
- IG incorporates feedback from September connectathon
- IG considered "implementable" by participating EHR vendors
- At least two server vendors propose timeline to beta deployment (i.e., at real sites)
- At least two client vendors propose timeline to beta deployment (i.e., at real sites)
- Hand off to ? (FHIR-I/US Core/Other HL7 entity for Publishing)