feat: implement Sleepy sleep diary
This commit is contained in:
86
lib/db.ts
Normal file
86
lib/db.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import Database from 'better-sqlite3';
|
||||
import { mkdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import type { SleepEntry } from './sleep';
|
||||
|
||||
const dataDirectory = process.env.DATA_DIR ?? join(process.cwd(), 'data');
|
||||
mkdirSync(dataDirectory, { recursive: true });
|
||||
|
||||
const database = new Database(join(dataDirectory, 'sleepy.db'));
|
||||
database.pragma('journal_mode = WAL');
|
||||
database.exec(`
|
||||
CREATE TABLE IF NOT EXISTS sleep_entries (
|
||||
date TEXT PRIMARY KEY NOT NULL,
|
||||
napped INTEGER NOT NULL DEFAULT 0,
|
||||
nap_start TEXT,
|
||||
nap_duration_minutes INTEGER,
|
||||
bedtime TEXT NOT NULL,
|
||||
sleep_latency_minutes INTEGER NOT NULL,
|
||||
awakenings INTEGER NOT NULL,
|
||||
waso_minutes INTEGER NOT NULL,
|
||||
rise_time TEXT NOT NULL,
|
||||
alarm_type TEXT NOT NULL,
|
||||
rating INTEGER NOT NULL,
|
||||
notes TEXT NOT NULL DEFAULT '',
|
||||
created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
`);
|
||||
|
||||
type SleepEntryRow = Omit<SleepEntry, 'napped' | 'napStart' | 'napDurationMinutes' | 'sleepLatencyMinutes' | 'wasoMinutes' | 'riseTime' | 'alarmType' | 'createdAt' | 'updatedAt'> & {
|
||||
napped: number;
|
||||
nap_start: string | null;
|
||||
nap_duration_minutes: number | null;
|
||||
sleep_latency_minutes: number;
|
||||
waso_minutes: number;
|
||||
rise_time: string;
|
||||
alarm_type: SleepEntry['alarmType'];
|
||||
created_at: string;
|
||||
updated_at: string;
|
||||
};
|
||||
|
||||
function mapEntry(row: SleepEntryRow): SleepEntry {
|
||||
return {
|
||||
date: row.date,
|
||||
napped: Boolean(row.napped),
|
||||
napStart: row.nap_start,
|
||||
napDurationMinutes: row.nap_duration_minutes,
|
||||
bedtime: row.bedtime,
|
||||
sleepLatencyMinutes: row.sleep_latency_minutes,
|
||||
awakenings: row.awakenings,
|
||||
wasoMinutes: row.waso_minutes,
|
||||
riseTime: row.rise_time,
|
||||
alarmType: row.alarm_type,
|
||||
rating: row.rating,
|
||||
notes: row.notes,
|
||||
createdAt: row.created_at,
|
||||
updatedAt: row.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
export function getEntry(date: string): SleepEntry | null {
|
||||
const row = database.prepare('SELECT * FROM sleep_entries WHERE date = ?').get(date) as SleepEntryRow | undefined;
|
||||
return row ? mapEntry(row) : null;
|
||||
}
|
||||
|
||||
export function saveEntry(entry: SleepEntry): SleepEntry {
|
||||
database.prepare(`
|
||||
INSERT INTO sleep_entries (
|
||||
date, napped, nap_start, nap_duration_minutes, bedtime, sleep_latency_minutes,
|
||||
awakenings, waso_minutes, rise_time, alarm_type, rating, notes
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT(date) DO UPDATE SET
|
||||
napped = excluded.napped, nap_start = excluded.nap_start,
|
||||
nap_duration_minutes = excluded.nap_duration_minutes, bedtime = excluded.bedtime,
|
||||
sleep_latency_minutes = excluded.sleep_latency_minutes, awakenings = excluded.awakenings,
|
||||
waso_minutes = excluded.waso_minutes, rise_time = excluded.rise_time,
|
||||
alarm_type = excluded.alarm_type, rating = excluded.rating, notes = excluded.notes,
|
||||
updated_at = CURRENT_TIMESTAMP
|
||||
`).run(
|
||||
entry.date, Number(entry.napped), entry.napStart, entry.napDurationMinutes, entry.bedtime,
|
||||
entry.sleepLatencyMinutes, entry.awakenings, entry.wasoMinutes, entry.riseTime,
|
||||
entry.alarmType, entry.rating, entry.notes,
|
||||
);
|
||||
|
||||
return getEntry(entry.date) ?? entry;
|
||||
}
|
||||
Reference in New Issue
Block a user