File size: 2,372 Bytes
4fd1234
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
CREATE EXTENSION IF NOT EXISTS "pgcrypto";

CREATE TABLE IF NOT EXISTS pages (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id TEXT NOT NULL,
    title TEXT NOT NULL,
    date DATE NOT NULL DEFAULT CURRENT_DATE,
    raw_text TEXT NOT NULL,
    annotations JSONB DEFAULT '{}'::jsonb,
    metadata JSONB DEFAULT '{}'::jsonb,   -- category, summary, tags (for smart browser)
    created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_pages_user ON pages(user_id, created_at DESC);
CREATE INDEX IF NOT EXISTS idx_pages_metadata ON pages USING gin(metadata);

CREATE TABLE IF NOT EXISTS concepts (
    id TEXT PRIMARY KEY,
    name TEXT NOT NULL,
    cefr_level TEXT NOT NULL,
    family TEXT NOT NULL,
    covered_on DATE
);

CREATE TABLE IF NOT EXISTS exercises (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id TEXT NOT NULL,
    page_id UUID REFERENCES pages(id) ON DELETE CASCADE,
    kind TEXT NOT NULL,
    prompt TEXT,
    model_answer TEXT,
    content JSONB DEFAULT '{}'::jsonb,
    created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_exercises_user ON exercises(user_id, created_at DESC);

-- Append-only participation ledger — never deduct points
CREATE TABLE IF NOT EXISTS points (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id TEXT NOT NULL,
    reason TEXT NOT NULL,
    amount INT NOT NULL CHECK (amount > 0),
    earned_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_points_user ON points(user_id, earned_at DESC);

-- Tracks which pre-generated sample images a user has already practiced
-- with, so the matched-image visual exercise (Day 4) doesn't repeat one
-- before cycling through the rest of the set.
CREATE TABLE IF NOT EXISTS user_image_usage (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id TEXT NOT NULL,
    image_id TEXT NOT NULL,
    used_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX IF NOT EXISTS idx_user_image_usage ON user_image_usage(user_id, image_id);

-- PRIVATE table (defined here but never written by the public Space)
CREATE TABLE IF NOT EXISTS mistakes (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    user_id TEXT NOT NULL,
    concept_id TEXT REFERENCES concepts(id),
    category TEXT NOT NULL,
    user_answer TEXT,
    correct_answer TEXT,
    explanation TEXT,
    made_on TIMESTAMPTZ DEFAULT NOW()
);