mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 06:06:10 +00:00
feat(fhir): serve Observations as a time series, code RMSSD and workouts
GET /api/fhir/Observation keeps its snapshot answer when no date is
sent. A date range (FHIR prefixes eq/gt/ge/lt/le, UTC when no offset)
switches it to the time series: every reading in the range plus
workouts, newest first, paged in the database through one UNION id
query and a count, so a page costs what it holds. Series ids are the
row's own (m-<id>, w-<id>) and read back through /Observation/{id}.
The series replays the same saved report selection as the snapshot,
pairs systolic and diastolic recorded at one instant into the
blood-pressure panel, leaves sleep stage segments out, and reads an
hourly heart-rate bucket as a mean over its hour. A code parameter
narrows either answer; a malformed date is a 400 OperationOutcome.
A workout is an activity Observation after the Physical Activity IG:
LOINC 73985-4 with the sport as its value and duration (55411-3) and
energy (41981-2) as components. RMSSD gets its own registry coding,
text-only because LOINC has no RMSSD term, so it never borrows the SDNN
code. The capability statement lists the two new parameters.
This commit is contained in:
1 parent
9e2a0681b2
commit
77619f8aa5
12 files changed
+1935
-26
No files matched your search
@@ -353,7 +353,7 @@ export const healthRecordPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["FHIR"],
|
||||
summary: "FHIR R4 CapabilityStatement (v1.11.0)",
|
||||
description:
|
||||
"Read-only FHIR R4 capability statement for the REST face. Declares the served resource types (Patient, Coverage, Observation, MedicationStatement, MedicationAdministration), their `read` and `search-type` interactions, the `Patient/$everything` operation, and the `application/fhir+json` format. Every advertised interaction has a route. Auth: `fhir:read` scope (cookie sessions also pass).",
|
||||
"Read-only FHIR R4 capability statement for the REST face. Declares the served resource types (Patient, Coverage, Observation, MedicationStatement, MedicationAdministration), their `read` and `search-type` interactions, the search parameters (`_count`, `_offset`; `date` and `code` on Observation), the `Patient/$everything` operation, and the `application/fhir+json` format. Every advertised interaction has a route. Auth: `fhir:read` scope (cookie sessions also pass).",
|
||||
responses: {
|
||||
"200": {
|
||||
description: "CapabilityStatement (application/fhir+json).",
|
||||
@@ -397,11 +397,13 @@ export const healthRecordPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["FHIR"],
|
||||
summary: "FHIR R4 Observation search (v1.11.0)",
|
||||
description:
|
||||
"Read-only `searchset` Bundle of the caller's own Observations (vitals / activity / lab / survey). Auth: `fhir:read` scope. Offset paging via `_count` (clamped ≤200) / `_offset`.",
|
||||
"Read-only `searchset` Bundle of the caller's own Observations (vitals / activity / lab / survey). Auth: `fhir:read` scope. Offset paging via `_count` (clamped ≤200) / `_offset`. Without `date` the answer is the snapshot it has always been: one latest reading per type plus the derived panels, ids `obs-N`. With `date` (v1.44, repeatable, FHIR prefixes eq/gt/ge/lt/le, a value without an offset read as UTC) the answer is the time series instead: every reading in the range plus workouts, newest first, paged in the database, ids `m-<id>` / `w-<id>` that read back through `/api/fhir/Observation/{id}`. The series follows the same saved report selection as the snapshot, leaves sleep out (stored as stage segments), pairs a systolic and diastolic recorded at the same instant into the blood-pressure panel, and codes a workout as an `activity` Observation (LOINC 73985-4 Exercise activity, duration 55411-3 and energy 41981-2 as components). `code` (repeatable, `system|code` or a bare code, comma-separated) narrows either answer; an unknown code matches nothing. A malformed `date` is a 400 OperationOutcome.",
|
||||
requestParams: {
|
||||
query: z.object({
|
||||
_count: z.coerce.number().optional(),
|
||||
_offset: z.coerce.number().optional(),
|
||||
date: z.string().optional(),
|
||||
code: z.string().optional(),
|
||||
}),
|
||||
},
|
||||
responses: {
|
||||
@@ -557,7 +559,7 @@ export const healthRecordPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
tags: ["FHIR"],
|
||||
summary: "FHIR R4 Observation read",
|
||||
description:
|
||||
"Read-only `Observation` by id, out of the caller's own record. An id belonging to someone else is indistinguishable from one that never existed — both answer a `not-found` OperationOutcome. Auth: `fhir:read` scope; `userId` is narrowed from auth.",
|
||||
"Read-only `Observation` by id, out of the caller's own record: a snapshot id (`obs-N`) or a series id (`m-<id>` / `w-<id>`, v1.44), the latter read inside the same saved selection the series search applies. An id belonging to someone else is indistinguishable from one that never existed — both answer a `not-found` OperationOutcome. Auth: `fhir:read` scope; `userId` is narrowed from auth.",
|
||||
requestParams: {
|
||||
path: z.object({ id: z.string() }),
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user