- DB: tasks table with scheduled_datetime, reminder_minutes_before, advance_notified, status - API: CRUD routes GET/POST /api/tasks, PATCH/DELETE /api/tasks/<id> - Scheduler: check_task_reminders() fires advance + at-time notifications, tracks advance_notified to prevent double-fire - Bot: handle_task() with add/list/done/cancel/delete actions + datetime resolution helper - AI: task interaction type + examples added to command_parser - Web: task list page with overdue/notified color coding + new task form with datetime-local picker - Nav: replaced Templates with Tasks in bottom nav Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
317 lines
11 KiB
SQL
317 lines
11 KiB
SQL
-- Users table (minimal)
|
|
CREATE TABLE IF NOT EXISTS users (
|
|
id UUID PRIMARY KEY,
|
|
username VARCHAR(255) UNIQUE NOT NULL,
|
|
password_hashed BYTEA NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Notifications table
|
|
CREATE TABLE IF NOT EXISTS notifications (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
discord_user_id VARCHAR(100),
|
|
discord_enabled BOOLEAN DEFAULT FALSE,
|
|
ntfy_topic VARCHAR(255),
|
|
ntfy_enabled BOOLEAN DEFAULT FALSE,
|
|
web_push_enabled BOOLEAN DEFAULT FALSE,
|
|
last_message_sent TIMESTAMP,
|
|
current_notification_status VARCHAR(50) DEFAULT 'inactive',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- Push subscriptions for web push notifications
|
|
CREATE TABLE IF NOT EXISTS push_subscriptions (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
endpoint TEXT NOT NULL,
|
|
p256dh TEXT NOT NULL,
|
|
auth TEXT NOT NULL,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- ── Routines ────────────────────────────────────────────────
|
|
|
|
CREATE TABLE IF NOT EXISTS routines (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
icon VARCHAR(100),
|
|
location VARCHAR(255),
|
|
environment_prompts JSON DEFAULT '[]',
|
|
habit_stack_after VARCHAR(255),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_steps (
|
|
id UUID PRIMARY KEY,
|
|
routine_id UUID REFERENCES routines(id) ON DELETE CASCADE,
|
|
name VARCHAR(255) NOT NULL,
|
|
duration_minutes INTEGER,
|
|
position INTEGER NOT NULL,
|
|
instructions TEXT,
|
|
step_type VARCHAR(50) DEFAULT 'generic',
|
|
media_url TEXT
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_sessions (
|
|
id UUID PRIMARY KEY,
|
|
routine_id UUID REFERENCES routines(id) ON DELETE CASCADE,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
status VARCHAR(20) DEFAULT 'active',
|
|
current_step_index INTEGER DEFAULT 0,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
paused_at TIMESTAMP,
|
|
completed_at TIMESTAMP,
|
|
abort_reason TEXT,
|
|
actual_duration_minutes INTEGER
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_schedules (
|
|
id UUID PRIMARY KEY,
|
|
routine_id UUID REFERENCES routines(id) ON DELETE CASCADE,
|
|
days JSON DEFAULT '[]',
|
|
time VARCHAR(5),
|
|
remind BOOLEAN DEFAULT FALSE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_session_notes (
|
|
id UUID PRIMARY KEY,
|
|
session_id UUID REFERENCES routine_sessions(id) ON DELETE CASCADE,
|
|
step_index INTEGER,
|
|
note TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_templates (
|
|
id UUID PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
icon VARCHAR(100),
|
|
category VARCHAR(50) DEFAULT 'Other',
|
|
created_by_admin BOOLEAN DEFAULT FALSE
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_template_steps (
|
|
id UUID PRIMARY KEY,
|
|
template_id UUID REFERENCES routine_templates(id) ON DELETE CASCADE,
|
|
name VARCHAR(255) NOT NULL,
|
|
instructions TEXT,
|
|
step_type VARCHAR(50) DEFAULT 'generic',
|
|
duration_minutes INTEGER,
|
|
media_url TEXT,
|
|
position INTEGER NOT NULL
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_tags (
|
|
id UUID PRIMARY KEY,
|
|
name VARCHAR(255) NOT NULL,
|
|
color VARCHAR(20) DEFAULT '#888888'
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_routine_tags (
|
|
routine_id UUID REFERENCES routines(id) ON DELETE CASCADE,
|
|
tag_id UUID REFERENCES routine_tags(id) ON DELETE CASCADE,
|
|
PRIMARY KEY (routine_id, tag_id)
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_streaks (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
routine_id UUID REFERENCES routines(id) ON DELETE CASCADE,
|
|
current_streak INTEGER DEFAULT 0,
|
|
longest_streak INTEGER DEFAULT 0,
|
|
last_completed_date DATE
|
|
);
|
|
|
|
-- ── Rewards ───────────────────────────────────────────────
|
|
|
|
CREATE TABLE IF NOT EXISTS reward_pool (
|
|
id UUID PRIMARY KEY,
|
|
category VARCHAR(50) NOT NULL,
|
|
content TEXT NOT NULL,
|
|
emoji VARCHAR(10),
|
|
rarity VARCHAR(20) DEFAULT 'common'
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS user_rewards (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
reward_id UUID REFERENCES reward_pool(id) ON DELETE CASCADE,
|
|
earned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
context VARCHAR(50)
|
|
);
|
|
|
|
-- ── User Preferences ──────────────────────────────────────
|
|
|
|
CREATE TABLE IF NOT EXISTS user_preferences (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
sound_enabled BOOLEAN DEFAULT FALSE,
|
|
haptic_enabled BOOLEAN DEFAULT TRUE,
|
|
show_launch_screen BOOLEAN DEFAULT TRUE,
|
|
celebration_style VARCHAR(50) DEFAULT 'standard',
|
|
timezone_offset INTEGER DEFAULT 0,
|
|
timezone_name VARCHAR(100),
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- ── Step Results (per-step tracking within sessions) ───────
|
|
|
|
CREATE TABLE IF NOT EXISTS routine_step_results (
|
|
id UUID PRIMARY KEY,
|
|
session_id UUID REFERENCES routine_sessions(id) ON DELETE CASCADE,
|
|
step_id UUID REFERENCES routine_steps(id) ON DELETE CASCADE,
|
|
step_index INTEGER NOT NULL,
|
|
result VARCHAR(20) NOT NULL,
|
|
duration_seconds INTEGER,
|
|
completed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- ── Medications ─────────────────────────────────────────────
|
|
|
|
CREATE TABLE IF NOT EXISTS medications (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
name VARCHAR(255) NOT NULL,
|
|
dosage VARCHAR(100) NOT NULL,
|
|
unit VARCHAR(50) NOT NULL,
|
|
frequency VARCHAR(50) NOT NULL,
|
|
times JSON DEFAULT '[]',
|
|
days_of_week JSON DEFAULT '[]',
|
|
interval_days INTEGER,
|
|
start_date DATE,
|
|
next_dose_date DATE,
|
|
notes TEXT,
|
|
active BOOLEAN DEFAULT TRUE,
|
|
quantity_remaining INTEGER,
|
|
refill_date DATE,
|
|
pharmacy_notes TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS med_logs (
|
|
id UUID PRIMARY KEY,
|
|
medication_id UUID REFERENCES medications(id) ON DELETE CASCADE,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
action VARCHAR(20) NOT NULL,
|
|
scheduled_time VARCHAR(5),
|
|
notes TEXT,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- ── Adaptive Medication Settings ─────────────────────────────
|
|
|
|
CREATE TABLE IF NOT EXISTS adaptive_med_settings (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
adaptive_timing_enabled BOOLEAN DEFAULT FALSE,
|
|
adaptive_mode VARCHAR(20) DEFAULT 'shift_all',
|
|
presence_tracking_enabled BOOLEAN DEFAULT FALSE,
|
|
nagging_enabled BOOLEAN DEFAULT TRUE,
|
|
nag_interval_minutes INTEGER DEFAULT 15,
|
|
max_nag_count INTEGER DEFAULT 4,
|
|
quiet_hours_start TIME,
|
|
quiet_hours_end TIME,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- ── User Discord Presence Tracking ────────────────────────────
|
|
|
|
CREATE TABLE IF NOT EXISTS user_presence (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
discord_user_id VARCHAR(255),
|
|
last_online_at TIMESTAMP,
|
|
last_offline_at TIMESTAMP,
|
|
is_currently_online BOOLEAN DEFAULT FALSE,
|
|
typical_wake_time TIME,
|
|
presence_history JSONB DEFAULT '[]',
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
-- ── Adaptive Medication Schedules (Daily Tracking) ───────────
|
|
|
|
CREATE TABLE IF NOT EXISTS medication_schedules (
|
|
id UUID PRIMARY KEY,
|
|
medication_id UUID REFERENCES medications(id) ON DELETE CASCADE,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
base_time TIME NOT NULL,
|
|
adjusted_time TIME,
|
|
adjustment_date DATE NOT NULL,
|
|
adjustment_minutes INTEGER DEFAULT 0,
|
|
nag_count INTEGER DEFAULT 0,
|
|
last_nag_at TIMESTAMP,
|
|
status VARCHAR(20) DEFAULT 'pending',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_med_schedules_user_date ON medication_schedules(user_uuid, adjustment_date);
|
|
CREATE INDEX IF NOT EXISTS idx_med_schedules_status ON medication_schedules(status);
|
|
|
|
-- ── Snitch System ─────────────────────────────────────────────
|
|
|
|
CREATE TABLE IF NOT EXISTS snitch_settings (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE UNIQUE,
|
|
snitch_enabled BOOLEAN DEFAULT FALSE,
|
|
trigger_after_nags INTEGER DEFAULT 4,
|
|
trigger_after_missed_doses INTEGER DEFAULT 1,
|
|
max_snitches_per_day INTEGER DEFAULT 2,
|
|
require_consent BOOLEAN DEFAULT TRUE,
|
|
consent_given BOOLEAN DEFAULT FALSE,
|
|
snitch_cooldown_hours INTEGER DEFAULT 4,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS snitch_contacts (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
contact_name VARCHAR(255) NOT NULL,
|
|
contact_type VARCHAR(50) NOT NULL,
|
|
contact_value VARCHAR(255) NOT NULL,
|
|
priority INTEGER DEFAULT 1,
|
|
notify_all BOOLEAN DEFAULT FALSE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
|
|
CREATE TABLE IF NOT EXISTS snitch_log (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
contact_id UUID REFERENCES snitch_contacts(id) ON DELETE SET NULL,
|
|
medication_id UUID REFERENCES medications(id) ON DELETE SET NULL,
|
|
trigger_reason VARCHAR(100) NOT NULL,
|
|
snitch_count_today INTEGER DEFAULT 1,
|
|
message_content TEXT,
|
|
sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
delivered BOOLEAN DEFAULT FALSE
|
|
);
|
|
|
|
CREATE INDEX IF NOT EXISTS idx_snitch_log_user_date ON snitch_log(user_uuid, DATE(sent_at));
|
|
CREATE INDEX IF NOT EXISTS idx_snitch_contacts_user ON snitch_contacts(user_uuid, is_active);
|
|
|
|
-- ── Migrations ──────────────────────────────────────────────
|
|
-- Add IANA timezone name to user preferences (run once on existing DBs)
|
|
ALTER TABLE user_preferences ADD COLUMN IF NOT EXISTS timezone_name VARCHAR(100);
|
|
|
|
-- ── Tasks (one-off appointments/reminders) ──────────────────
|
|
CREATE TABLE IF NOT EXISTS tasks (
|
|
id UUID PRIMARY KEY,
|
|
user_uuid UUID REFERENCES users(id) ON DELETE CASCADE,
|
|
title VARCHAR(255) NOT NULL,
|
|
description TEXT,
|
|
scheduled_datetime TIMESTAMP NOT NULL,
|
|
reminder_minutes_before INTEGER DEFAULT 15,
|
|
advance_notified BOOLEAN DEFAULT FALSE,
|
|
status VARCHAR(20) DEFAULT 'pending',
|
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
);
|
|
CREATE INDEX IF NOT EXISTS idx_tasks_user_scheduled ON tasks(user_uuid, scheduled_datetime);
|
|
CREATE INDEX IF NOT EXISTS idx_tasks_pending ON tasks(status) WHERE status = 'pending';
|