mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-08 12:32:43 +00:00
style: apply prettier across the tree
One-shot prettier pass so the new format:check gate starts from a clean baseline. Formatting only — no behaviour change. Generator-owned and vendored artifacts stay untouched via .prettierignore.
This commit is contained in:
1 parent
b3fea2ef59
commit
1f4ba5939d
1000 files changed
+12130
-8385
No files matched your search
@@ -9,11 +9,7 @@
|
||||
* `self.__APP_VERSION__` (falling back to a literal).
|
||||
*/
|
||||
import { execFileSync } from "node:child_process";
|
||||
import {
|
||||
existsSync,
|
||||
readFileSync,
|
||||
rmSync,
|
||||
} from "node:fs";
|
||||
import { existsSync, readFileSync, rmSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { describe, it, expect, afterAll } from "vitest";
|
||||
|
||||
|
||||
@@ -59,7 +59,9 @@ async function main(): Promise<void> {
|
||||
const dryRun = !args.confirm;
|
||||
|
||||
if (dryRun) {
|
||||
console.log("[backfill-note] DRY RUN — no rows will be written. Pass --confirm to commit.");
|
||||
console.log(
|
||||
"[backfill-note] DRY RUN — no rows will be written. Pass --confirm to commit.",
|
||||
);
|
||||
} else {
|
||||
console.log("[backfill-note] COMMIT MODE — updating rows in place.");
|
||||
}
|
||||
@@ -92,7 +94,9 @@ async function main(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[backfill-note] ${updated} row${updated === 1 ? "" : "s"} ${dryRun ? "would be" : "were"} updated`);
|
||||
console.log(
|
||||
`[backfill-note] ${updated} row${updated === 1 ? "" : "s"} ${dryRun ? "would be" : "were"} updated`,
|
||||
);
|
||||
await prisma.$disconnect();
|
||||
}
|
||||
|
||||
|
||||
@@ -66,12 +66,9 @@ async function main(): Promise<void> {
|
||||
for (const [index, user] of users.entries()) {
|
||||
const userStarted = Date.now();
|
||||
try {
|
||||
const { rowsUpserted, durationMs } = await recomputeUserRollups(
|
||||
user.id,
|
||||
{
|
||||
granularities: ALL_GRANULARITIES,
|
||||
},
|
||||
);
|
||||
const { rowsUpserted, durationMs } = await recomputeUserRollups(user.id, {
|
||||
granularities: ALL_GRANULARITIES,
|
||||
});
|
||||
totalRowsUpserted += rowsUpserted;
|
||||
console.log(
|
||||
`[${index + 1}/${users.length}] user=${user.username} ` +
|
||||
|
||||
@@ -158,11 +158,7 @@ export function checkEnv(
|
||||
// All-or-none groups: surface a synthetic row when the group is
|
||||
// partially populated. This catches the v1.4.40 AP-2 pattern even
|
||||
// when the group itself is `required: false`.
|
||||
if (
|
||||
group.allOrNone &&
|
||||
presentInGroup > 0 &&
|
||||
absentInGroup > 0
|
||||
) {
|
||||
if (group.allOrNone && presentInGroup > 0 && absentInGroup > 0) {
|
||||
results.push({
|
||||
group: group.name,
|
||||
variable: "<all-or-none>",
|
||||
@@ -205,8 +201,7 @@ function renderResults(results: CheckResult[]): {
|
||||
);
|
||||
} else if (r.required) {
|
||||
console.log(
|
||||
` [MISSING-REQUIRED] ${r.variable}` +
|
||||
(r.note ? ` — ${r.note}` : ""),
|
||||
` [MISSING-REQUIRED] ${r.variable}` + (r.note ? ` — ${r.note}` : ""),
|
||||
);
|
||||
missingRequired++;
|
||||
} else {
|
||||
|
||||
@@ -163,11 +163,17 @@
|
||||
"required": false,
|
||||
"allOrNone": true,
|
||||
"variables": [
|
||||
{ "name": "BACKUP_S3_ENDPOINT", "purpose": "S3-compatible endpoint URL." },
|
||||
{
|
||||
"name": "BACKUP_S3_ENDPOINT",
|
||||
"purpose": "S3-compatible endpoint URL."
|
||||
},
|
||||
{ "name": "BACKUP_S3_BUCKET", "purpose": "Bucket name." },
|
||||
{ "name": "BACKUP_S3_ACCESS_KEY", "purpose": "S3 access key." },
|
||||
{ "name": "BACKUP_S3_SECRET_KEY", "purpose": "S3 secret key." },
|
||||
{ "name": "BACKUP_ENCRYPTION_KEY", "purpose": "Separate AES key for the backup body — MUST NOT match ENCRYPTION_KEY." }
|
||||
{
|
||||
"name": "BACKUP_ENCRYPTION_KEY",
|
||||
"purpose": "Separate AES key for the backup body — MUST NOT match ENCRYPTION_KEY."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
@@ -53,4 +53,6 @@ const rewritten = sw.replace(fallbackPattern, `$1${JSON.stringify(version)}`);
|
||||
if (rewritten !== sw) writeFileSync(swPath, rewritten, "utf8");
|
||||
|
||||
// One-line log so the build output shows the version we baked in.
|
||||
process.stdout.write(`sw-version.js → ${version} (sw.js fallback re-anchored)\n`);
|
||||
process.stdout.write(
|
||||
`sw-version.js → ${version} (sw.js fallback re-anchored)\n`,
|
||||
);
|
||||
@@ -30,28 +30,28 @@ on both ends** so secret columns can be NULLed and column order is pinned.
|
||||
|
||||
## What moves (FK-parent-first, 20 tables)
|
||||
|
||||
| # | Table | Scope predicate |
|
||||
|---|---|---|
|
||||
| 1 | `users` | `id = demo` (the one row; secrets NULLed, `role='ADMIN'`) |
|
||||
| 2 | `measurements` | `user_id = demo AND deleted_at IS NULL` (incl. RECOVERY/STRAIN/STRESS_SCORE rows) |
|
||||
| 3 | `cycle_profiles` | `user_id = demo` |
|
||||
| 4 | `menstrual_cycles` | `user_id = demo` |
|
||||
| 5 | `cycle_day_logs` | `user_id = demo` (encrypted cols NULLed) |
|
||||
| 6 | `cycle_symptom_links` | `day_log_id IN (demo's day logs)` |
|
||||
| 7 | `mood_entries` | `user_id = demo AND deleted_at IS NULL` |
|
||||
| 8 | `mood_entry_tag_links` | `mood_entry_id IN (demo's entries)` — global tags only |
|
||||
| 9 | `medications` | `user_id = demo` |
|
||||
| 10 | `medication_schedules` | `medication_id IN (demo's meds)` |
|
||||
| 11 | `medication_intake_events` | `user_id = demo AND deleted_at IS NULL` |
|
||||
| 12 | `medication_dose_changes` | `medication_id IN (demo's meds)` |
|
||||
| 13 | `medication_inventory_items` | `user_id = demo` |
|
||||
| 14 | `medication_inventory_events` | `medication_id IN (demo's meds)` |
|
||||
| 15 | `medication_side_effects` | `user_id = demo` |
|
||||
| 16 | `reminder_phase_configs` | `medication_id IN (demo's meds)` |
|
||||
| 17 | `personal_records` | `user_id = demo` |
|
||||
| 18 | `user_achievements` | `user_id = demo` |
|
||||
| 19 | `consent_receipts` | `user_id = demo` (carries the `ai_full` consent) |
|
||||
| 20 | `audit_logs` | `user_id = demo AND action LIKE 'insights.%'` (plaintext AI assessments) |
|
||||
| # | Table | Scope predicate |
|
||||
| --- | ----------------------------- | --------------------------------------------------------------------------------- |
|
||||
| 1 | `users` | `id = demo` (the one row; secrets NULLed, `role='ADMIN'`) |
|
||||
| 2 | `measurements` | `user_id = demo AND deleted_at IS NULL` (incl. RECOVERY/STRAIN/STRESS_SCORE rows) |
|
||||
| 3 | `cycle_profiles` | `user_id = demo` |
|
||||
| 4 | `menstrual_cycles` | `user_id = demo` |
|
||||
| 5 | `cycle_day_logs` | `user_id = demo` (encrypted cols NULLed) |
|
||||
| 6 | `cycle_symptom_links` | `day_log_id IN (demo's day logs)` |
|
||||
| 7 | `mood_entries` | `user_id = demo AND deleted_at IS NULL` |
|
||||
| 8 | `mood_entry_tag_links` | `mood_entry_id IN (demo's entries)` — global tags only |
|
||||
| 9 | `medications` | `user_id = demo` |
|
||||
| 10 | `medication_schedules` | `medication_id IN (demo's meds)` |
|
||||
| 11 | `medication_intake_events` | `user_id = demo AND deleted_at IS NULL` |
|
||||
| 12 | `medication_dose_changes` | `medication_id IN (demo's meds)` |
|
||||
| 13 | `medication_inventory_items` | `user_id = demo` |
|
||||
| 14 | `medication_inventory_events` | `medication_id IN (demo's meds)` |
|
||||
| 15 | `medication_side_effects` | `user_id = demo` |
|
||||
| 16 | `reminder_phase_configs` | `medication_id IN (demo's meds)` |
|
||||
| 17 | `personal_records` | `user_id = demo` |
|
||||
| 18 | `user_achievements` | `user_id = demo` |
|
||||
| 19 | `consent_receipts` | `user_id = demo` (carries the `ai_full` consent) |
|
||||
| 20 | `audit_logs` | `user_id = demo AND action LIKE 'insights.%'` (plaintext AI assessments) |
|
||||
|
||||
## Secret / credential columns NULLed on the `users` row
|
||||
|
||||
@@ -142,7 +142,7 @@ be a write-capable admin. Keep DEMO_MODE enabled.
|
||||
|
||||
## Caveats / things to confirm before running
|
||||
|
||||
- **`FOREIGN_HITS` assertion** counts *all* non-demo rows in the carried
|
||||
- **`FOREIGN_HITS` assertion** counts _all_ non-demo rows in the carried
|
||||
tables on edge01, not just what this run inserted. On a clean
|
||||
single-demo-user DB it is `0`. If edge01 legitimately hosts other demo-only
|
||||
accounts, treat a non-zero value as a warning to inspect, not an automatic
|
||||
|
||||
@@ -151,7 +151,11 @@ async function updateCoolifyEnv(
|
||||
appUuid: string,
|
||||
value: string,
|
||||
): Promise<void> {
|
||||
const list = await coolifyRequest(base, token, `/applications/${appUuid}/envs`);
|
||||
const list = await coolifyRequest(
|
||||
base,
|
||||
token,
|
||||
`/applications/${appUuid}/envs`,
|
||||
);
|
||||
if (!list.ok) {
|
||||
fail(`Coolify env list failed: HTTP ${list.status} — check token/UUID`);
|
||||
}
|
||||
|
||||
@@ -100,7 +100,10 @@ const TAG = "[repair-intake-anomalies]";
|
||||
// LOCAL time by default; this parser pins them back to UTC so every
|
||||
// Date this script handles is the true instant. (OID 1114 = timestamp
|
||||
// without time zone.)
|
||||
types.setTypeParser(1114, (value: string) => new Date(`${value.replace(" ", "T")}Z`));
|
||||
types.setTypeParser(
|
||||
1114,
|
||||
(value: string) => new Date(`${value.replace(" ", "T")}Z`),
|
||||
);
|
||||
|
||||
/**
|
||||
* Serialise a Date for a `timestamp without time zone` comparison /
|
||||
@@ -1056,11 +1059,7 @@ async function main(): Promise<void> {
|
||||
SET "starts_on" = $2::timestamp, "updated_at" = NOW()
|
||||
WHERE "id" = $1 AND "starts_on" = $3::timestamp
|
||||
`,
|
||||
[
|
||||
p.medicationId,
|
||||
utcParam(p.firstRow),
|
||||
utcParam(p.startsOn),
|
||||
],
|
||||
[p.medicationId, utcParam(p.firstRow), utcParam(p.startsOn)],
|
||||
);
|
||||
summary.startsOnAdjusted += res.rowCount ?? 0;
|
||||
}
|
||||
@@ -1105,11 +1104,17 @@ async function main(): Promise<void> {
|
||||
console.log(` duplicate slot groups: ${summary.duplicateGroups}`);
|
||||
console.log(` duplicate rows tombstoned: ${summary.rowsTombstoned}`);
|
||||
console.log(` implausible taken_at rows: ${summary.implausibleRows}`);
|
||||
console.log(` implausible rows tombstoned: ${summary.implausibleTombstoned}`);
|
||||
console.log(` window/times drift schedules: ${summary.windowDriftSchedules}`);
|
||||
console.log(
|
||||
` implausible rows tombstoned: ${summary.implausibleTombstoned}`,
|
||||
);
|
||||
console.log(
|
||||
` window/times drift schedules: ${summary.windowDriftSchedules}`,
|
||||
);
|
||||
console.log(` windows reconciled: ${summary.windowsReconciled}`);
|
||||
console.log(` stale-anchor pending rows: ${summary.staleAnchorRows}`);
|
||||
console.log(` stale-anchor rows tombstoned: ${summary.staleAnchorTombstoned}`);
|
||||
console.log(
|
||||
` stale-anchor rows tombstoned: ${summary.staleAnchorTombstoned}`,
|
||||
);
|
||||
console.log(` era-inference candidates: ${summary.eraCandidates}`);
|
||||
console.log(` era revisions created: ${summary.eraRevisionsCreated}`);
|
||||
console.log(` startsOn candidates: ${summary.startsOnCandidates}`);
|
||||
|
||||
@@ -207,7 +207,11 @@ async function main() {
|
||||
),
|
||||
);
|
||||
results.push(
|
||||
await rotateStringColumn("WhoopConnection", field, prisma.whoopConnection),
|
||||
await rotateStringColumn(
|
||||
"WhoopConnection",
|
||||
field,
|
||||
prisma.whoopConnection,
|
||||
),
|
||||
);
|
||||
results.push(
|
||||
await rotateStringColumn(
|
||||
@@ -349,11 +353,7 @@ async function main() {
|
||||
await rotateBytesColumn("LabResult", "noteEncrypted", prisma.labResult),
|
||||
);
|
||||
results.push(
|
||||
await rotateBytesColumn(
|
||||
"Biomarker",
|
||||
"contextEncrypted",
|
||||
prisma.biomarker,
|
||||
),
|
||||
await rotateBytesColumn("Biomarker", "contextEncrypted", prisma.biomarker),
|
||||
);
|
||||
results.push(
|
||||
await rotateBytesColumn(
|
||||
|
||||
+189
-34
@@ -194,17 +194,41 @@ async function seed() {
|
||||
// Weight: 86.5 → trending down to a stable ~82 (healthy BMI ~24.7 at 182cm)
|
||||
const weights = randomWalk(86.5, 82.0, span, 0.6);
|
||||
// Systolic BP: 128 → settled in the optimal band ~118 (clamped)
|
||||
const sysBP = randomWalk(128, 118, span, 3, VALUE_RANGES.BLOOD_PRESSURE_SYS);
|
||||
const sysBP = randomWalk(
|
||||
128,
|
||||
118,
|
||||
span,
|
||||
3,
|
||||
VALUE_RANGES.BLOOD_PRESSURE_SYS,
|
||||
);
|
||||
// Diastolic BP: 82 → settled ~76 (clamped)
|
||||
const diaBP = randomWalk(82, 76, span, 2.5, VALUE_RANGES.BLOOD_PRESSURE_DIA);
|
||||
const diaBP = randomWalk(
|
||||
82,
|
||||
76,
|
||||
span,
|
||||
2.5,
|
||||
VALUE_RANGES.BLOOD_PRESSURE_DIA,
|
||||
);
|
||||
// Spot pulse (daytime heart rate): ~72, gently lower
|
||||
const pulse = randomWalk(74, 70, span, 3, VALUE_RANGES.PULSE);
|
||||
// Resting heart rate: clean ~60, the metric the resting-pulse tile scores
|
||||
const restingHr = randomWalk(64, 58, span, 2, VALUE_RANGES.RESTING_HEART_RATE);
|
||||
const restingHr = randomWalk(
|
||||
64,
|
||||
58,
|
||||
span,
|
||||
2,
|
||||
VALUE_RANGES.RESTING_HEART_RATE,
|
||||
);
|
||||
// Body fat: 24% → trending to a healthy ~19%
|
||||
const bodyFat = randomWalk(24.0, 19.0, span, 0.4, VALUE_RANGES.BODY_FAT);
|
||||
// Sleep duration in MINUTES (the SLEEP_DURATION unit): ~7h → ~7h45m
|
||||
const sleepMin = randomWalk(420, 465, span, 25, VALUE_RANGES.SLEEP_DURATION);
|
||||
const sleepMin = randomWalk(
|
||||
420,
|
||||
465,
|
||||
span,
|
||||
25,
|
||||
VALUE_RANGES.SLEEP_DURATION,
|
||||
);
|
||||
// Steps: 6500 → a solid ~9000
|
||||
const steps = randomWalk(6500, 9000, span, 1500);
|
||||
|
||||
@@ -330,7 +354,8 @@ async function seed() {
|
||||
const leanMass = fatFree;
|
||||
// Total body water ≈ 73% of fat-free mass (kg of water).
|
||||
const bodyWater = Math.round(fatFree * 0.73 * 10) / 10;
|
||||
const boneMass = Math.round((3.1 + (Math.random() - 0.5) * 0.2) * 10) / 10;
|
||||
const boneMass =
|
||||
Math.round((3.1 + (Math.random() - 0.5) * 0.2) * 10) / 10;
|
||||
// Visceral-fat rating on Withings' 1–12 scale; a healthy ~6, easing
|
||||
// down slightly as the body fat trends down.
|
||||
const visceralFat = Math.round(7 - (i / span) * 1.5);
|
||||
@@ -396,29 +421,77 @@ async function seed() {
|
||||
// FLIGHTS_CLIMBED count. Source APPLE_HEALTH device_type 'watch'.
|
||||
console.log("Creating cardio fitness + vitals series...");
|
||||
// HRV SDNN: a healthy ~55 ms, gently rising; also write the RMSSD variant.
|
||||
const hrvSdnn = randomWalk(48, 58, span, 5, VALUE_RANGES.HEART_RATE_VARIABILITY);
|
||||
const hrvSdnn = randomWalk(
|
||||
48,
|
||||
58,
|
||||
span,
|
||||
5,
|
||||
VALUE_RANGES.HEART_RATE_VARIABILITY,
|
||||
);
|
||||
const hrvRmssd = randomWalk(42, 52, span, 5, VALUE_RANGES.HRV_RMSSD);
|
||||
// SpO2: 97–99%.
|
||||
const spo2 = randomWalk(98, 98, span, 0.8, VALUE_RANGES.OXYGEN_SATURATION);
|
||||
// Respiratory rate: 13–16 breaths/min.
|
||||
const respRate = randomWalk(15, 14, span, 0.8, VALUE_RANGES.RESPIRATORY_RATE);
|
||||
const respRate = randomWalk(
|
||||
15,
|
||||
14,
|
||||
span,
|
||||
0.8,
|
||||
VALUE_RANGES.RESPIRATORY_RATE,
|
||||
);
|
||||
// Active energy: ~550 kcal/day.
|
||||
const activeKcal = randomWalk(480, 600, span, 80, VALUE_RANGES.ACTIVE_ENERGY_BURNED);
|
||||
const activeKcal = randomWalk(
|
||||
480,
|
||||
600,
|
||||
span,
|
||||
80,
|
||||
VALUE_RANGES.ACTIVE_ENERGY_BURNED,
|
||||
);
|
||||
// Walking + running distance (metres): ~6–8 km/day, tracks steps.
|
||||
const distM = randomWalk(6200, 7800, span, 900, VALUE_RANGES.WALKING_RUNNING_DISTANCE);
|
||||
const distM = randomWalk(
|
||||
6200,
|
||||
7800,
|
||||
span,
|
||||
900,
|
||||
VALUE_RANGES.WALKING_RUNNING_DISTANCE,
|
||||
);
|
||||
// Flights climbed: ~8–14/day.
|
||||
const flights = randomWalk(9, 13, span, 4, VALUE_RANGES.FLIGHTS_CLIMBED);
|
||||
|
||||
for (let i = 0; i < span; i++) {
|
||||
const date = daysAgo(days - i);
|
||||
const vitals: Array<{ type: string; value: number; unit: string }> = [
|
||||
{ type: "HEART_RATE_VARIABILITY", value: Math.round(hrvSdnn[i]), unit: "ms" },
|
||||
{
|
||||
type: "HEART_RATE_VARIABILITY",
|
||||
value: Math.round(hrvSdnn[i]),
|
||||
unit: "ms",
|
||||
},
|
||||
{ type: "HRV_RMSSD", value: Math.round(hrvRmssd[i]), unit: "ms" },
|
||||
{ type: "OXYGEN_SATURATION", value: Math.round(spo2[i] * 10) / 10, unit: "%" },
|
||||
{ type: "RESPIRATORY_RATE", value: Math.round(respRate[i] * 10) / 10, unit: "count/min" },
|
||||
{ type: "ACTIVE_ENERGY_BURNED", value: Math.round(activeKcal[i]), unit: "kcal" },
|
||||
{ type: "WALKING_RUNNING_DISTANCE", value: Math.round(distM[i]), unit: "m" },
|
||||
{ type: "FLIGHTS_CLIMBED", value: Math.round(flights[i]), unit: "count" },
|
||||
{
|
||||
type: "OXYGEN_SATURATION",
|
||||
value: Math.round(spo2[i] * 10) / 10,
|
||||
unit: "%",
|
||||
},
|
||||
{
|
||||
type: "RESPIRATORY_RATE",
|
||||
value: Math.round(respRate[i] * 10) / 10,
|
||||
unit: "count/min",
|
||||
},
|
||||
{
|
||||
type: "ACTIVE_ENERGY_BURNED",
|
||||
value: Math.round(activeKcal[i]),
|
||||
unit: "kcal",
|
||||
},
|
||||
{
|
||||
type: "WALKING_RUNNING_DISTANCE",
|
||||
value: Math.round(distM[i]),
|
||||
unit: "m",
|
||||
},
|
||||
{
|
||||
type: "FLIGHTS_CLIMBED",
|
||||
value: Math.round(flights[i]),
|
||||
unit: "count",
|
||||
},
|
||||
];
|
||||
for (const v of vitals) {
|
||||
await client.query(
|
||||
@@ -449,19 +522,47 @@ async function seed() {
|
||||
console.log("Creating recovery / strain / sleep scores...");
|
||||
const recovery = randomWalk(68, 78, span, 8, VALUE_RANGES.RECOVERY_SCORE);
|
||||
const dayStrain = randomWalk(11, 13, span, 2.5, VALUE_RANGES.DAY_STRAIN);
|
||||
const sleepPerf = randomWalk(82, 90, span, 6, VALUE_RANGES.SLEEP_PERFORMANCE);
|
||||
const sleepPerf = randomWalk(
|
||||
82,
|
||||
90,
|
||||
span,
|
||||
6,
|
||||
VALUE_RANGES.SLEEP_PERFORMANCE,
|
||||
);
|
||||
const sleepEff = randomWalk(88, 93, span, 4, VALUE_RANGES.SLEEP_EFFICIENCY);
|
||||
const sleepConsistency = randomWalk(74, 84, span, 6, VALUE_RANGES.SLEEP_CONSISTENCY);
|
||||
const sleepConsistency = randomWalk(
|
||||
74,
|
||||
84,
|
||||
span,
|
||||
6,
|
||||
VALUE_RANGES.SLEEP_CONSISTENCY,
|
||||
);
|
||||
for (let i = 0; i < span; i++) {
|
||||
// Anchor scores to the morning wake instant so a night's recovery sits
|
||||
// on the day it belongs to.
|
||||
const at = daysAgoAt(days - i, 7, 0);
|
||||
const scores: Array<{ type: string; value: number; unit: string }> = [
|
||||
{ type: "RECOVERY_SCORE", value: Math.round(recovery[i]), unit: "score" },
|
||||
{ type: "DAY_STRAIN", value: Math.round(dayStrain[i] * 10) / 10, unit: "score" },
|
||||
{ type: "SLEEP_PERFORMANCE", value: Math.round(sleepPerf[i]), unit: "%" },
|
||||
{
|
||||
type: "RECOVERY_SCORE",
|
||||
value: Math.round(recovery[i]),
|
||||
unit: "score",
|
||||
},
|
||||
{
|
||||
type: "DAY_STRAIN",
|
||||
value: Math.round(dayStrain[i] * 10) / 10,
|
||||
unit: "score",
|
||||
},
|
||||
{
|
||||
type: "SLEEP_PERFORMANCE",
|
||||
value: Math.round(sleepPerf[i]),
|
||||
unit: "%",
|
||||
},
|
||||
{ type: "SLEEP_EFFICIENCY", value: Math.round(sleepEff[i]), unit: "%" },
|
||||
{ type: "SLEEP_CONSISTENCY", value: Math.round(sleepConsistency[i]), unit: "%" },
|
||||
{
|
||||
type: "SLEEP_CONSISTENCY",
|
||||
value: Math.round(sleepConsistency[i]),
|
||||
unit: "%",
|
||||
},
|
||||
];
|
||||
for (const s of scores) {
|
||||
await client.query(
|
||||
@@ -490,9 +591,33 @@ async function seed() {
|
||||
distanceM: number | null;
|
||||
strain: number;
|
||||
}> = [
|
||||
{ sport: "running", durationMin: 38, kcal: 420, avgHr: 148, maxHr: 171, distanceM: 6500, strain: 13.4 },
|
||||
{ sport: "strength", durationMin: 52, kcal: 360, avgHr: 118, maxHr: 152, distanceM: null, strain: 10.2 },
|
||||
{ sport: "cycling", durationMin: 65, kcal: 520, avgHr: 134, maxHr: 158, distanceM: 24000, strain: 12.1 },
|
||||
{
|
||||
sport: "running",
|
||||
durationMin: 38,
|
||||
kcal: 420,
|
||||
avgHr: 148,
|
||||
maxHr: 171,
|
||||
distanceM: 6500,
|
||||
strain: 13.4,
|
||||
},
|
||||
{
|
||||
sport: "strength",
|
||||
durationMin: 52,
|
||||
kcal: 360,
|
||||
avgHr: 118,
|
||||
maxHr: 152,
|
||||
distanceM: null,
|
||||
strain: 10.2,
|
||||
},
|
||||
{
|
||||
sport: "cycling",
|
||||
durationMin: 65,
|
||||
kcal: 520,
|
||||
avgHr: 134,
|
||||
maxHr: 158,
|
||||
distanceM: 24000,
|
||||
strain: 12.1,
|
||||
},
|
||||
];
|
||||
// Step backwards through the window placing ~2 sessions a week per slot
|
||||
// so the cadence lands around 3–4/week without overlapping.
|
||||
@@ -503,7 +628,11 @@ async function seed() {
|
||||
const plan = workoutPlan[planIdx % workoutPlan.length];
|
||||
planIdx += 1;
|
||||
const startHour = 17 + Math.floor(Math.random() * 3);
|
||||
const startedAt = daysAgoAt(dayOffset, startHour, Math.floor(Math.random() * 50));
|
||||
const startedAt = daysAgoAt(
|
||||
dayOffset,
|
||||
startHour,
|
||||
Math.floor(Math.random() * 50),
|
||||
);
|
||||
const durationSec = plan.durationMin * 60;
|
||||
const endedAt = new Date(startedAt.getTime() + durationSec * 1000);
|
||||
const workoutId = cuid();
|
||||
@@ -755,12 +884,24 @@ async function seed() {
|
||||
low: number;
|
||||
high: number;
|
||||
}> = [
|
||||
{ analyte: "Total Cholesterol", value: 178, unit: "mg/dL", low: 0, high: 200 },
|
||||
{
|
||||
analyte: "Total Cholesterol",
|
||||
value: 178,
|
||||
unit: "mg/dL",
|
||||
low: 0,
|
||||
high: 200,
|
||||
},
|
||||
{ analyte: "LDL", value: 98, unit: "mg/dL", low: 0, high: 130 },
|
||||
{ analyte: "HDL", value: 58, unit: "mg/dL", low: 40, high: 100 },
|
||||
{ analyte: "Triglycerides", value: 92, unit: "mg/dL", low: 0, high: 150 },
|
||||
{ analyte: "HbA1c", value: 5.2, unit: "%", low: 4.0, high: 5.6 },
|
||||
{ analyte: "Fasting Glucose", value: 89, unit: "mg/dL", low: 70, high: 99 },
|
||||
{
|
||||
analyte: "Fasting Glucose",
|
||||
value: 89,
|
||||
unit: "mg/dL",
|
||||
low: 70,
|
||||
high: 99,
|
||||
},
|
||||
{ analyte: "Vitamin D", value: 42, unit: "ng/mL", low: 30, high: 100 },
|
||||
{ analyte: "TSH", value: 1.8, unit: "mIU/L", low: 0.4, high: 4.0 },
|
||||
{ analyte: "Ferritin", value: 120, unit: "ng/mL", low: 30, high: 400 },
|
||||
@@ -919,14 +1060,28 @@ async function seed() {
|
||||
await client.query("COMMIT");
|
||||
console.log("\nDemo data seeded successfully!");
|
||||
console.log(` User: demo / demo123demo123`);
|
||||
console.log(` Measurements: weight, BP, pulse, resting HR, body fat, steps, sleep`);
|
||||
console.log(` Body composition: fat/lean/muscle mass, water, bone, visceral fat, BMI`);
|
||||
console.log(` Blood glucose: ${glucoseReadings.length} readings/day (fasting + post-meal + bedtime)`);
|
||||
console.log(` Cardio + vitals: HRV (SDNN + RMSSD), SpO2, resp. rate, VO2max, active energy, distance, flights`);
|
||||
console.log(` Scores: recovery, day strain, sleep performance/efficiency/consistency`);
|
||||
console.log(` Sleep: 7 per-stage nights (4 stages each) + daily aggregate`);
|
||||
console.log(
|
||||
` Measurements: weight, BP, pulse, resting HR, body fat, steps, sleep`,
|
||||
);
|
||||
console.log(
|
||||
` Body composition: fat/lean/muscle mass, water, bone, visceral fat, BMI`,
|
||||
);
|
||||
console.log(
|
||||
` Blood glucose: ${glucoseReadings.length} readings/day (fasting + post-meal + bedtime)`,
|
||||
);
|
||||
console.log(
|
||||
` Cardio + vitals: HRV (SDNN + RMSSD), SpO2, resp. rate, VO2max, active energy, distance, flights`,
|
||||
);
|
||||
console.log(
|
||||
` Scores: recovery, day strain, sleep performance/efficiency/consistency`,
|
||||
);
|
||||
console.log(
|
||||
` Sleep: 7 per-stage nights (4 stages each) + daily aggregate`,
|
||||
);
|
||||
console.log(` Medications: 3 (high compliance, today on-track)`);
|
||||
console.log(` Workouts: ~3-4/week (running, strength, cycling) with HR samples`);
|
||||
console.log(
|
||||
` Workouts: ~3-4/week (running, strength, cycling) with HR samples`,
|
||||
);
|
||||
console.log(` Mood: ~${Math.round(span * 0.95)} entries`);
|
||||
console.log(` Vorsorge reminders: 2 (dental, annual physical)`);
|
||||
console.log(` Lab panel: ${labResults.length} biomarkers`);
|
||||
|
||||
Reference in new issue
Block a user