mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 20:42:43 +00:00
docs(api): describe the dimensionless lab unit on every lab unit field
The unit on lab results, biomarkers, the scanned-report commit and the document fact edit now states that "1" means the value has no unit, that it matches only itself, and that an empty unit stays refused. The lab CSV import description names the 1 in the unit column. Refs #1313
This commit is contained in:
1 parent
f9431da64f
commit
e605fe0ae8
8 files changed
+79
-14
No files matched your search
+55
-4
@@ -15875,10 +15875,11 @@ paths:
|
||||
checks an hour, the import itself the shared 5-an-hour `import:` bucket. Owner only; refused with
|
||||
`module.disabled` when the module is switched off. An unknown query parameter is refused with 422, so a misspelt
|
||||
`dryRun` never writes. Each row's name resolves to the account's marker the way a scanned report's rows do
|
||||
(name, alias, catalog name); a new analyte mints one marker from its first row. A numeric result needs a unit;
|
||||
one in another unit is converted when the conversion is known and keeps the printed value beside it, otherwise
|
||||
the row is refused with `unit_mismatch`. Any other text in the value column is a qualitative result. A date
|
||||
without a time is stored as that calendar date. At most 20 000 data rows."
|
||||
(name, alias, catalog name); a new analyte mints one marker from its first row. A numeric result needs a unit:
|
||||
an empty unit cell is refused with `missing_unit`, and `1` (the UCUM unity) states a value without a unit, such
|
||||
as a ratio or INR. A result in another unit is converted when the conversion is known and keeps the printed
|
||||
value beside it, otherwise the row is refused with `unit_mismatch`. Any other text in the value column is a
|
||||
qualitative result. A date without a time is stored as that calendar date. At most 20 000 data rows."
|
||||
parameters:
|
||||
- in: query
|
||||
name: dryRun
|
||||
@@ -34146,6 +34147,11 @@ components:
|
||||
minLength: 1
|
||||
maxLength: 120
|
||||
unit:
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and a
|
||||
unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in a real
|
||||
unit and the other way round. Clients show nothing beside such a value. An empty unit on a numeric value is
|
||||
never read as `"1"` and stays refused.'
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
@@ -34215,6 +34221,11 @@ components:
|
||||
minLength: 1
|
||||
maxLength: 120
|
||||
unit:
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and a
|
||||
unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in a real
|
||||
unit and the other way round. Clients show nothing beside such a value. An empty unit on a numeric value is
|
||||
never read as `"1"` and stays refused.'
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
@@ -34309,6 +34320,11 @@ components:
|
||||
minLength: 1
|
||||
maxLength: 120
|
||||
unit:
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice
|
||||
and a unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker
|
||||
kept in a real unit and the other way round. Clients show nothing beside such a value. An empty unit
|
||||
on a numeric value is never read as `"1"` and stays refused.'
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
@@ -34363,6 +34379,11 @@ components:
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and a
|
||||
unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in a real
|
||||
unit and the other way round. Clients show nothing beside such a value. An empty unit on a numeric value is
|
||||
never read as `"1"` and stays refused.'
|
||||
lowerBound:
|
||||
type: number
|
||||
upperBound:
|
||||
@@ -34392,6 +34413,11 @@ components:
|
||||
minLength: 1
|
||||
maxLength: 120
|
||||
unit:
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and a
|
||||
unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in a real
|
||||
unit and the other way round. Clients show nothing beside such a value. An empty unit on a numeric value is
|
||||
never read as `"1"` and stays refused.'
|
||||
type: string
|
||||
minLength: 1
|
||||
maxLength: 40
|
||||
@@ -35759,6 +35785,11 @@ components:
|
||||
maxLength: 300
|
||||
- type: "null"
|
||||
unit:
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and
|
||||
a unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in
|
||||
a real unit and the other way round. Clients show nothing beside such a value. An empty unit on a
|
||||
numeric value is never read as `"1"` and stays refused.'
|
||||
anyOf:
|
||||
- type: string
|
||||
maxLength: 80
|
||||
@@ -75275,6 +75306,11 @@ components:
|
||||
- "null"
|
||||
unit:
|
||||
type: string
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and a
|
||||
unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in a real
|
||||
unit and the other way round. Clients show nothing beside such a value. An empty unit on a numeric value is
|
||||
never read as `"1"` and stays refused.'
|
||||
converted:
|
||||
type: boolean
|
||||
sourceValue:
|
||||
@@ -75524,6 +75560,11 @@ components:
|
||||
- "null"
|
||||
unit:
|
||||
type: string
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and a
|
||||
unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in a real
|
||||
unit and the other way round. Clients show nothing beside such a value. An empty unit on a numeric value is
|
||||
never read as `"1"` and stays refused.'
|
||||
converted:
|
||||
type: boolean
|
||||
sourceValue:
|
||||
@@ -76503,6 +76544,11 @@ components:
|
||||
type: string
|
||||
unit:
|
||||
type: string
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and a
|
||||
unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in a real
|
||||
unit and the other way round. Clients show nothing beside such a value. An empty unit on a numeric value is
|
||||
never read as `"1"` and stays refused.'
|
||||
lowerBound:
|
||||
type:
|
||||
- number
|
||||
@@ -76602,6 +76648,11 @@ components:
|
||||
type: string
|
||||
unit:
|
||||
type: string
|
||||
description: 'The unit the value is in, free-form as the report prints it (e.g. `mg/dL`). `"1"`, the UCUM unity, means
|
||||
the value has no unit: a ratio or an index such as INR or cholesterol/HDL. It is an explicit choice and a
|
||||
unit of its own: it matches only `"1"`, so a reading without a unit is refused under a marker kept in a real
|
||||
unit and the other way round. Clients show nothing beside such a value. An empty unit on a numeric value is
|
||||
never read as `"1"` and stays refused.'
|
||||
lowerBound:
|
||||
type:
|
||||
- number
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
import type { ZodOpenApiObject } from "zod-openapi";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { LAB_UNIT_DESCRIPTION } from "@/lib/labs/dimensionless";
|
||||
|
||||
import {
|
||||
createBiomarkerSchema,
|
||||
updateBiomarkerSchema,
|
||||
@@ -54,7 +56,7 @@ const biomarkerRow = z
|
||||
.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
unit: z.string(),
|
||||
unit: z.string().describe(LAB_UNIT_DESCRIPTION),
|
||||
lowerBound: z.number().nullable(),
|
||||
upperBound: z.number().nullable(),
|
||||
panel: z.string().nullable(),
|
||||
|
||||
@@ -553,7 +553,7 @@ export const importPaths: NonNullable<ZodOpenApiObject["paths"]> = {
|
||||
post: {
|
||||
tags: ["Import"],
|
||||
summary: "Import lab results from CSV",
|
||||
description: `${RECORD_IMPORT_COMMON} Each row's name resolves to the account's marker the way a scanned report's rows do (name, alias, catalog name); a new analyte mints one marker from its first row. A numeric result needs a unit; one in another unit is converted when the conversion is known and keeps the printed value beside it, otherwise the row is refused with \`unit_mismatch\`. Any other text in the value column is a qualitative result. A date without a time is stored as that calendar date. At most 20 000 data rows.`,
|
||||
description: `${RECORD_IMPORT_COMMON} Each row's name resolves to the account's marker the way a scanned report's rows do (name, alias, catalog name); a new analyte mints one marker from its first row. A numeric result needs a unit: an empty unit cell is refused with \`missing_unit\`, and \`1\` (the UCUM unity) states a value without a unit, such as a ratio or INR. A result in another unit is converted when the conversion is known and keeps the printed value beside it, otherwise the row is refused with \`unit_mismatch\`. Any other text in the value column is a qualitative result. A date without a time is stored as that calendar date. At most 20 000 data rows.`,
|
||||
requestParams: { query: labsCsvQuerySchema },
|
||||
requestBody: {
|
||||
required: true,
|
||||
|
||||
@@ -11,6 +11,8 @@ import { withRelated } from "./relation-links";
|
||||
import type { ZodOpenApiObject } from "zod-openapi";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { LAB_UNIT_DESCRIPTION } from "@/lib/labs/dimensionless";
|
||||
|
||||
import {
|
||||
createLabResultSchema,
|
||||
labOverviewQuerySchema,
|
||||
@@ -71,7 +73,7 @@ const labResultRow = z
|
||||
// exactly one of the two is non-null per row.
|
||||
value: z.number().nullable(),
|
||||
valueText: z.string().nullable(),
|
||||
unit: z.string(),
|
||||
unit: z.string().describe(LAB_UNIT_DESCRIPTION),
|
||||
converted: z.boolean(),
|
||||
sourceValue: z.number().nullable(),
|
||||
sourceUnit: z.string().nullable(),
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { LAB_UNIT_DESCRIPTION } from "@/lib/labs/dimensionless";
|
||||
|
||||
const requiredText = (max: number) => z.string().trim().min(1).max(max);
|
||||
|
||||
const lowerBound = z.number().finite().optional();
|
||||
@@ -40,7 +42,7 @@ const optionalPanel = z
|
||||
export const createBiomarkerSchema = z
|
||||
.object({
|
||||
name: requiredText(120),
|
||||
unit: requiredText(40),
|
||||
unit: requiredText(40).describe(LAB_UNIT_DESCRIPTION),
|
||||
lowerBound,
|
||||
upperBound,
|
||||
context: optionalContext,
|
||||
@@ -67,7 +69,7 @@ export type CreateBiomarkerInput = z.infer<typeof createBiomarkerSchema>;
|
||||
export const updateBiomarkerSchema = z
|
||||
.object({
|
||||
name: requiredText(120).optional(),
|
||||
unit: requiredText(40).optional(),
|
||||
unit: requiredText(40).optional().describe(LAB_UNIT_DESCRIPTION),
|
||||
lowerBound: lowerBound.or(z.null()),
|
||||
upperBound: upperBound.or(z.null()),
|
||||
context: optionalContext.or(z.null()),
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
import { locales } from "@/lib/i18n/config";
|
||||
import { LAB_UNIT_DESCRIPTION } from "@/lib/labs/dimensionless";
|
||||
import {
|
||||
DOCUMENT_BULK_MAX_IDS,
|
||||
DOCUMENT_CONTENT_INDEX_SOURCES,
|
||||
@@ -34,7 +35,6 @@ import {
|
||||
type DocumentContentIndexSourceValue,
|
||||
type InboundDocumentKindValue,
|
||||
} from "@/lib/documents/inbound-constants";
|
||||
|
||||
import type { AiCapabilityState } from "@/lib/ai/capabilities/types";
|
||||
import { OCR_MAX_ROWS } from "@/lib/validations/labs-ocr";
|
||||
|
||||
@@ -443,7 +443,7 @@ const observationEditSchema = z
|
||||
codeSystem: optCode,
|
||||
value: z.number().finite().nullable().optional(),
|
||||
valueText: optText(300),
|
||||
unit: optText(80),
|
||||
unit: optText(80).describe(LAB_UNIT_DESCRIPTION),
|
||||
referenceLow: z.number().finite().nullable().optional(),
|
||||
referenceHigh: z.number().finite().nullable().optional(),
|
||||
referenceText: optText(120),
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
import type { AiCapabilityState } from "@/lib/ai/capabilities/types";
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { LAB_UNIT_DESCRIPTION } from "@/lib/labs/dimensionless";
|
||||
import type { WrittenOutcome } from "@/lib/outcome/written-outcome";
|
||||
|
||||
import { validateEntryInstant } from "@/lib/validations/entry-instant";
|
||||
@@ -257,7 +258,13 @@ export const ocrCommitRowSchema = z
|
||||
.or(z.literal("").transform(() => undefined)),
|
||||
value: z.number().finite().optional(),
|
||||
valueText: z.string().trim().min(1).max(OCR_VALUE_TEXT_MAX).optional(),
|
||||
unit: z.string().trim().min(1).max(OCR_UNIT_MAX).optional(),
|
||||
unit: z
|
||||
.string()
|
||||
.trim()
|
||||
.min(1)
|
||||
.max(OCR_UNIT_MAX)
|
||||
.optional()
|
||||
.describe(LAB_UNIT_DESCRIPTION),
|
||||
referenceLow,
|
||||
referenceHigh,
|
||||
/**
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
*/
|
||||
import { z } from "zod/v4";
|
||||
|
||||
import { LAB_UNIT_DESCRIPTION } from "@/lib/labs/dimensionless";
|
||||
import { SOURCE_REFERENCE_TEXT_MAX } from "@/lib/labs/parse-reference-range";
|
||||
import { validateEntryInstant } from "@/lib/validations/entry-instant";
|
||||
import { isoDateTime } from "@/lib/validations/datetime";
|
||||
@@ -123,7 +124,7 @@ export const createLabResultSchema = z
|
||||
// (`value`) OR qualitative (`valueText`), never both, never neither.
|
||||
value: z.number().finite().optional(),
|
||||
valueText,
|
||||
unit: requiredText(40).optional(),
|
||||
unit: requiredText(40).optional().describe(LAB_UNIT_DESCRIPTION),
|
||||
referenceLow,
|
||||
referenceHigh,
|
||||
sourceReferenceLow,
|
||||
@@ -208,7 +209,7 @@ export const updateLabResultSchema = z
|
||||
// row's type (numeric ↔ qualitative) is intentionally not supported here —
|
||||
// delete and re-add — so an omitted key leaves the row's existing type.
|
||||
valueText: requiredText(120).optional(),
|
||||
unit: requiredText(40).optional(),
|
||||
unit: requiredText(40).optional().describe(LAB_UNIT_DESCRIPTION),
|
||||
referenceLow: referenceLow.or(z.null()),
|
||||
referenceHigh: referenceHigh.or(z.null()),
|
||||
sourceReferenceLow: sourceReferenceLow.or(z.null()),
|
||||
|
||||
Reference in new issue
Block a user