feat(db): lab_drafts, lab_draws, biomarkers tables
Refs PAI-91.
This commit is contained in:
33
server/drizzle/0001_labs.sql
Normal file
33
server/drizzle/0001_labs.sql
Normal file
@@ -0,0 +1,33 @@
|
||||
CREATE TABLE `biomarkers` (
|
||||
`id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
`draw_id` text NOT NULL,
|
||||
`panel` text NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`marker` text NOT NULL,
|
||||
`analyte_key` text,
|
||||
`value` text NOT NULL,
|
||||
`value_num` real,
|
||||
`unit` text,
|
||||
`reference_range` text,
|
||||
`flagged` integer DEFAULT 0 NOT NULL,
|
||||
`value_canonical` real,
|
||||
`canonical_unit` text
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `lab_drafts` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`filename` text NOT NULL,
|
||||
`file_path` text NOT NULL,
|
||||
`status` text NOT NULL,
|
||||
`extracted` text,
|
||||
`error` text,
|
||||
`created_at` integer NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE `lab_draws` (
|
||||
`id` text PRIMARY KEY NOT NULL,
|
||||
`collected_at` text NOT NULL,
|
||||
`lab_name` text,
|
||||
`draft_id` text,
|
||||
`created_at` integer NOT NULL
|
||||
);
|
||||
279
server/drizzle/meta/0001_snapshot.json
Normal file
279
server/drizzle/meta/0001_snapshot.json
Normal file
@@ -0,0 +1,279 @@
|
||||
{
|
||||
"version": "6",
|
||||
"dialect": "sqlite",
|
||||
"id": "8d0cf0e5-668d-4526-a0cb-5a264d813be3",
|
||||
"prevId": "383d7bd7-3ebf-456e-8c5c-f39cebab14c4",
|
||||
"tables": {
|
||||
"biomarkers": {
|
||||
"name": "biomarkers",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "integer",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": true
|
||||
},
|
||||
"draw_id": {
|
||||
"name": "draw_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"panel": {
|
||||
"name": "panel",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"name": {
|
||||
"name": "name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"marker": {
|
||||
"name": "marker",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"analyte_key": {
|
||||
"name": "analyte_key",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value_num": {
|
||||
"name": "value_num",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"unit": {
|
||||
"name": "unit",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"reference_range": {
|
||||
"name": "reference_range",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"flagged": {
|
||||
"name": "flagged",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false,
|
||||
"default": 0
|
||||
},
|
||||
"value_canonical": {
|
||||
"name": "value_canonical",
|
||||
"type": "real",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"canonical_unit": {
|
||||
"name": "canonical_unit",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"lab_drafts": {
|
||||
"name": "lab_drafts",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"filename": {
|
||||
"name": "filename",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"file_path": {
|
||||
"name": "file_path",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"status": {
|
||||
"name": "status",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"extracted": {
|
||||
"name": "extracted",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"error": {
|
||||
"name": "error",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"lab_draws": {
|
||||
"name": "lab_draws",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"collected_at": {
|
||||
"name": "collected_at",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"lab_name": {
|
||||
"name": "lab_name",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"draft_id": {
|
||||
"name": "draft_id",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": false,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"sessions": {
|
||||
"name": "sessions",
|
||||
"columns": {
|
||||
"id": {
|
||||
"name": "id",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"created_at": {
|
||||
"name": "created_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"expires_at": {
|
||||
"name": "expires_at",
|
||||
"type": "integer",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
},
|
||||
"settings": {
|
||||
"name": "settings",
|
||||
"columns": {
|
||||
"key": {
|
||||
"name": "key",
|
||||
"type": "text",
|
||||
"primaryKey": true,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
},
|
||||
"value": {
|
||||
"name": "value",
|
||||
"type": "text",
|
||||
"primaryKey": false,
|
||||
"notNull": true,
|
||||
"autoincrement": false
|
||||
}
|
||||
},
|
||||
"indexes": {},
|
||||
"foreignKeys": {},
|
||||
"compositePrimaryKeys": {},
|
||||
"uniqueConstraints": {},
|
||||
"checkConstraints": {}
|
||||
}
|
||||
},
|
||||
"views": {},
|
||||
"enums": {},
|
||||
"_meta": {
|
||||
"schemas": {},
|
||||
"tables": {},
|
||||
"columns": {}
|
||||
},
|
||||
"internal": {
|
||||
"indexes": {}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,13 @@
|
||||
"when": 1787000672259,
|
||||
"tag": "0000_init",
|
||||
"breakpoints": true
|
||||
},
|
||||
{
|
||||
"idx": 1,
|
||||
"version": "6",
|
||||
"when": 1787005028172,
|
||||
"tag": "0001_labs",
|
||||
"breakpoints": true
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
import { integer, real, sqliteTable, text } from "drizzle-orm/sqlite-core";
|
||||
|
||||
export const settings = sqliteTable("settings", {
|
||||
key: text("key").primaryKey(),
|
||||
@@ -10,3 +10,37 @@ export const sessions = sqliteTable("sessions", {
|
||||
createdAt: integer("created_at").notNull(),
|
||||
expiresAt: integer("expires_at").notNull(),
|
||||
});
|
||||
|
||||
export const labDrafts = sqliteTable("lab_drafts", {
|
||||
id: text("id").primaryKey(),
|
||||
filename: text("filename").notNull(),
|
||||
filePath: text("file_path").notNull(),
|
||||
status: text("status").notNull(), // pending | confirmed | discarded
|
||||
extracted: text("extracted"),
|
||||
error: text("error"),
|
||||
createdAt: integer("created_at").notNull(),
|
||||
});
|
||||
|
||||
export const labDraws = sqliteTable("lab_draws", {
|
||||
id: text("id").primaryKey(),
|
||||
collectedAt: text("collected_at").notNull(), // ISO date YYYY-MM-DD
|
||||
labName: text("lab_name"),
|
||||
draftId: text("draft_id"),
|
||||
createdAt: integer("created_at").notNull(),
|
||||
});
|
||||
|
||||
export const biomarkers = sqliteTable("biomarkers", {
|
||||
id: integer("id").primaryKey({ autoIncrement: true }),
|
||||
drawId: text("draw_id").notNull(),
|
||||
panel: text("panel").notNull(),
|
||||
name: text("name").notNull(),
|
||||
marker: text("marker").notNull(),
|
||||
analyteKey: text("analyte_key"),
|
||||
value: text("value").notNull(),
|
||||
valueNum: real("value_num"),
|
||||
unit: text("unit"),
|
||||
referenceRange: text("reference_range"),
|
||||
flagged: integer("flagged").notNull().default(0),
|
||||
valueCanonical: real("value_canonical"),
|
||||
canonicalUnit: text("canonical_unit"),
|
||||
});
|
||||
|
||||
21
server/test/db-labs.test.ts
Normal file
21
server/test/db-labs.test.ts
Normal file
@@ -0,0 +1,21 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { openDb } from "../src/db";
|
||||
import { biomarkers, labDraws } from "../src/db/schema";
|
||||
|
||||
describe("labs schema", () => {
|
||||
test("draw + biomarker round-trip", async () => {
|
||||
const db = openDb(mkdtempSync(join(tmpdir(), "helios-")));
|
||||
await db.insert(labDraws).values({ id: "d1", collectedAt: "2026-01-15", labName: "Acme Lab", draftId: null, createdAt: 1 });
|
||||
await db.insert(biomarkers).values({
|
||||
drawId: "d1", panel: "lipids", name: "LDL Cholesterol", marker: "ldl", analyteKey: "ldl",
|
||||
value: "3.1", valueNum: 3.1, unit: "mmol/L", referenceRange: "< 3.4", flagged: 0,
|
||||
valueCanonical: 3.1, canonicalUnit: "mmol/L",
|
||||
});
|
||||
const rows = await db.select().from(biomarkers);
|
||||
expect(rows).toHaveLength(1);
|
||||
expect(rows[0].marker).toBe("ldl");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user