Files
Synculous-2/config/schema.sql
chelsea bebc609091 Add one-off tasks/appointments feature
- 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>
2026-02-19 16:43:42 -06:00

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';