Files
FabledScribe/alembic/versions/0017_add_projects.py
T
bvandeusen 012eb1d46b Add Projects, Milestones, RAG auto-inject, push notifications, PWA, tag normalisation
## Projects & Milestones (Phases A + G)
- New models: Project, Milestone (Project → Milestone → Task hierarchy)
- notes table: project_id + milestone_id FKs; parent_id FK constraint activated
- Migrations: 0017 (projects), 0018 (push_subscriptions), 0019 (events), 0020 (milestones)
- Services: projects.py, milestones.py (CRUD + progress tracking)
- Routes: /api/projects + /api/projects/<id>/milestones
- LLM tools: create/list/get/update project; create/list milestone; project + milestone + parent_task params on note/task tools
- Frontend: ProjectListView (stacked milestone bars), ProjectView (milestone-grouped kanban), ProjectSelector, MilestoneSelector, NoteEditorView + TaskEditorView updated

## RAG Auto-injection (Phase B)
- Notes ≥0.60 cosine similarity auto-injected into system prompt (max 3, 800 chars each)
- excluded_note_ids param; ChatView "Auto-included" sidebar section

## Summarisation improvements (Phase C)
- Threshold 20→30, keep-recent 6→8, max_tokens 200→400
- Two-pass summarisation for histories >50 messages

## Browser push notifications (Phase E)
- PushSubscription model + migration; pywebpush dependency
- /api/push routes; VAPID config; fire-and-forget on generation complete
- Frontend: sw.js, push store, Settings toggle

## PWA manifest (Phase F)
- manifest.json, Apple meta tags, service worker registration in main.ts

## Tag normalisation
- All tags lowercased + deduplicated at backend (create_note/update_note) and frontend (TagInput sanitize)
- Note/Task types gain project_id + milestone_id fields; store signatures updated

## CalDAV
- Radicale embedded server reverted; back to user-configured external CalDAV

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 20:52:21 -05:00

49 lines
1.9 KiB
Python

"""Add projects table and project_id to notes."""
from alembic import op
revision = "0017"
down_revision = "0016"
def upgrade() -> None:
op.execute("""
CREATE TABLE IF NOT EXISTS projects (
id SERIAL PRIMARY KEY,
user_id INTEGER REFERENCES users(id) ON DELETE CASCADE,
title TEXT NOT NULL DEFAULT '',
description TEXT NOT NULL DEFAULT '',
goal TEXT NOT NULL DEFAULT '',
status TEXT NOT NULL DEFAULT 'active',
color TEXT,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
)
""")
op.execute("CREATE INDEX IF NOT EXISTS ix_projects_user_id ON projects(user_id)")
op.execute("CREATE INDEX IF NOT EXISTS ix_projects_status ON projects(status)")
op.execute("ALTER TABLE notes ADD COLUMN IF NOT EXISTS project_id INTEGER REFERENCES projects(id) ON DELETE SET NULL")
op.execute("CREATE INDEX IF NOT EXISTS ix_notes_project_id ON notes(project_id)")
# Add FK constraint to existing parent_id column (was nullable with no FK)
# Only add if constraint doesn't already exist
op.execute("""
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM pg_constraint WHERE conname = 'notes_parent_id_fkey'
) THEN
ALTER TABLE notes ADD CONSTRAINT notes_parent_id_fkey
FOREIGN KEY (parent_id) REFERENCES notes(id) ON DELETE SET NULL;
END IF;
END $$;
""")
def downgrade() -> None:
op.execute("ALTER TABLE notes DROP CONSTRAINT IF EXISTS notes_parent_id_fkey")
op.execute("DROP INDEX IF EXISTS ix_notes_project_id")
op.execute("ALTER TABLE notes DROP COLUMN IF EXISTS project_id")
op.execute("DROP INDEX IF EXISTS ix_projects_status")
op.execute("DROP INDEX IF EXISTS ix_projects_user_id")
op.execute("DROP TABLE IF EXISTS projects")