File size: 11,442 Bytes
3a338e5
2f230ab
3a338e5
 
 
 
2f230ab
3a338e5
2f230ab
3a338e5
 
 
2f230ab
3a338e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f230ab
3a338e5
2f230ab
3a338e5
 
 
 
 
 
 
 
 
 
 
 
 
 
2f230ab
 
 
 
 
 
3a338e5
 
 
2f230ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3a338e5
 
09ab9b3
2f230ab
3a338e5
 
 
 
 
 
 
 
 
 
 
2f230ab
 
 
 
 
 
 
 
 
 
 
3a338e5
 
 
2f230ab
3a338e5
2f230ab
 
3a338e5
 
09ab9b3
3a338e5
 
 
 
 
2f230ab
 
 
 
 
 
3a338e5
 
 
 
 
 
 
 
2f230ab
 
 
 
 
 
3a338e5
2f230ab
 
 
 
 
3a338e5
2f230ab
 
 
 
3a338e5
2f230ab
 
 
 
 
3a338e5
2f230ab
 
 
3a338e5
 
 
 
 
 
2f230ab
3a338e5
 
 
 
 
 
 
 
2f230ab
3a338e5
 
 
 
 
 
 
 
 
 
 
 
2f230ab
 
 
 
 
 
 
 
 
 
 
 
 
 
3a338e5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2f230ab
 
 
 
 
 
 
 
 
 
 
3a338e5
 
 
 
 
 
 
2f230ab
3a338e5
2f230ab
3a338e5
 
2f230ab
3a338e5
 
 
 
 
 
2f230ab
3a338e5
 
 
 
 
2f230ab
3a338e5
 
2f230ab
3a338e5
 
 
 
 
 
 
 
 
 
2f230ab
3a338e5
 
 
2f230ab
 
 
 
 
 
 
 
 
 
 
 
 
3a338e5
2f230ab
 
 
 
3a338e5
 
2f230ab
3a338e5
 
 
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
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
-- =====================================================
-- Supabase 초기 μ„€μ • 톡합 슀크립트 (μ™„μ „ μ΄ˆκΈ°ν™” 버전)
-- κ°„ν˜Έ μΈμˆ˜μΈκ³„ ꡐ윑 ν”Œλž«νΌ
-- μ‹€ν–‰ μˆœμ„œ: 이 νŒŒμΌμ„ Supabase SQL Editorμ—μ„œ ν•œ 번만 μ‹€ν–‰ν•˜μ„Έμš”
-- =====================================================


-- =====================================================
-- 1. κΈ°λ³Έ ν…Œμ΄λΈ” 생성 (μ˜μ‘΄μ„± μˆœμ„œλŒ€λ‘œ)
-- =====================================================

-- 1.1 patients ν…Œμ΄λΈ” (ν™˜μž κΈ°λ³Έ 정보)
CREATE TABLE patients (
    id VARCHAR(50) PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    age INTEGER NOT NULL,
    gender VARCHAR(10) NOT NULL,
    weight INTEGER,
    height INTEGER,
    diagnosis VARCHAR(200) NOT NULL,
    admission_date DATE NOT NULL,
    attending_physician VARCHAR(100),
    allergies VARCHAR(200),
    comorbidities TEXT,
    room_number VARCHAR(20),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
);

COMMENT ON TABLE patients IS 'ν™˜μž κΈ°λ³Έ 정보';

-- 1.2 scenarios ν…Œμ΄λΈ” (μΈμˆ˜μΈκ³„ μ‹œλ‚˜λ¦¬μ˜€)
CREATE TABLE scenarios (
    id VARCHAR(50) PRIMARY KEY,
    patient_id VARCHAR(50) NOT NULL,
    title VARCHAR(200) NOT NULL,
    day INTEGER NOT NULL,
    handoff_situation VARCHAR(200),
    vitals JSONB,
    labs JSONB,
    orders JSONB,
    nursing_notes JSONB,
    physical_exam JSONB,
    imaging JSONB,
    surgery_info JSONB,
    discharge_education JSONB,
    hospital_day INTEGER,
    status_badge VARCHAR(20),
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    CONSTRAINT scenarios_patient_fkey 
        FOREIGN KEY (patient_id) 
        REFERENCES patients(id) 
        ON DELETE CASCADE 
        ON UPDATE CASCADE
);

COMMENT ON TABLE scenarios IS 'μΈμˆ˜μΈκ³„ μ‹œλ‚˜λ¦¬μ˜€';

-- 1.3 sessions ν…Œμ΄λΈ” (μ„Έμ…˜ 관리) - session_idλ₯Ό PK둜
CREATE TABLE sessions (
    session_id VARCHAR(100) PRIMARY KEY,
    student_id VARCHAR(100) NOT NULL,
    scenario_id VARCHAR(50) NOT NULL,
    status VARCHAR(20) DEFAULT 'active',
    started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    ended_at TIMESTAMP WITH TIME ZONE,
    created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    CONSTRAINT sessions_scenario_fkey 
        FOREIGN KEY (scenario_id) 
        REFERENCES scenarios(id) 
        ON DELETE RESTRICT 
        ON UPDATE CASCADE
);

COMMENT ON TABLE sessions IS 'TSID 기반 μ±„νŒ… μ„Έμ…˜ 관리 ν…Œμ΄λΈ”';
COMMENT ON COLUMN sessions.session_id IS 'TSID 기반 μ„Έμ…˜ μ‹λ³„μž (PRIMARY KEY)';

-- 1.4 chat_history ν…Œμ΄λΈ” (학생과 AI κ°„μ˜ μ±„νŒ… 기둝)
CREATE TABLE chat_history (
    id SERIAL PRIMARY KEY,
    session_id VARCHAR(100) NOT NULL,
    student_id VARCHAR(100) NOT NULL,
    scenario_id VARCHAR(50) NOT NULL,
    role VARCHAR(20) NOT NULL CHECK (role IN ('user', 'assistant')),
    message TEXT NOT NULL,
    timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    CONSTRAINT chat_history_session_fkey 
        FOREIGN KEY (session_id) 
        REFERENCES sessions(session_id) 
        ON DELETE CASCADE 
        ON UPDATE CASCADE
);

COMMENT ON TABLE chat_history IS '학생과 AI κ°„μ˜ μ±„νŒ… 기둝';

-- 1.5 handoff_records ν…Œμ΄λΈ” (μΈμˆ˜μΈκ³„ 기둝 및 평가)
CREATE TABLE handoff_records (
    id SERIAL PRIMARY KEY,
    session_id VARCHAR(100),
    student_id VARCHAR(100) NOT NULL,
    scenario_id VARCHAR(50) NOT NULL,
    submitted_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    situation TEXT,
    background TEXT,
    assessment TEXT,
    recommendation TEXT,
    total_score FLOAT,
    category_scores JSONB,
    strengths JSONB,
    improvements JSONB,
    detailed_feedback TEXT,
    missing_critical_info JSONB,
    safety_concerns JSONB,
    CONSTRAINT handoff_records_scenario_fkey 
        FOREIGN KEY (scenario_id) 
        REFERENCES scenarios(id) 
        ON DELETE RESTRICT 
        ON UPDATE CASCADE,
    CONSTRAINT handoff_records_session_fkey 
        FOREIGN KEY (session_id) 
        REFERENCES sessions(session_id) 
        ON DELETE SET NULL 
        ON UPDATE CASCADE
);

COMMENT ON TABLE handoff_records IS 'ν•™μƒμ˜ μΈμˆ˜μΈκ³„ 기둝 및 AI 평가 κ²°κ³Ό';
COMMENT ON COLUMN handoff_records.session_id IS 'μ±„νŒ… μ„Έμ…˜ ID (nullable)';

-- 1.6 sbar_drafts ν…Œμ΄λΈ” (μž‘μ„± 쀑인 SBAR μž„μ‹œ μ €μž₯)
CREATE TABLE sbar_drafts (
    id SERIAL PRIMARY KEY,
    session_id VARCHAR(100) NOT NULL UNIQUE,
    student_id VARCHAR(100) NOT NULL,
    scenario_id VARCHAR(50) NOT NULL,
    situation TEXT DEFAULT '',
    background TEXT DEFAULT '',
    assessment TEXT DEFAULT '',
    recommendation TEXT DEFAULT '',
    updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
    CONSTRAINT sbar_drafts_session_fkey 
        FOREIGN KEY (session_id) 
        REFERENCES sessions(session_id) 
        ON DELETE CASCADE 
        ON UPDATE CASCADE
);

COMMENT ON TABLE sbar_drafts IS 'μž‘μ„± 쀑인 SBAR μž„μ‹œ μ €μž₯';

-- =====================================================
-- 2. 인덱슀 생성 (μ„±λŠ₯ μ΅œμ ν™”)
-- =====================================================

-- patients
CREATE INDEX idx_patients_name ON patients(name);

-- scenarios
CREATE INDEX idx_scenarios_patient ON scenarios(patient_id);
CREATE INDEX idx_scenarios_day ON scenarios(day);

-- sessions
CREATE INDEX idx_sessions_student ON sessions(student_id);
CREATE INDEX idx_sessions_scenario ON sessions(scenario_id);
CREATE INDEX idx_sessions_status ON sessions(status);
CREATE INDEX idx_sessions_started ON sessions(started_at DESC);

-- chat_history
CREATE INDEX idx_chat_history_session ON chat_history(session_id);
CREATE INDEX idx_chat_history_student ON chat_history(student_id);
CREATE INDEX idx_chat_history_timestamp ON chat_history(timestamp);

-- handoff_records
CREATE INDEX idx_handoff_student ON handoff_records(student_id);
CREATE INDEX idx_handoff_scenario ON handoff_records(scenario_id);
CREATE INDEX idx_handoff_session ON handoff_records(session_id);
CREATE INDEX idx_handoff_submitted ON handoff_records(submitted_at DESC);

-- sbar_drafts
CREATE INDEX idx_sbar_session ON sbar_drafts(session_id);
CREATE INDEX idx_sbar_student ON sbar_drafts(student_id);

-- =====================================================
-- 3. 트리거 ν•¨μˆ˜ 및 트리거 생성
-- =====================================================

-- updated_at μžλ™ μ—…λ°μ΄νŠΈ ν•¨μˆ˜
CREATE FUNCTION update_updated_at_column()
RETURNS TRIGGER AS $$
BEGIN
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- μ„Έμ…˜ μ’…λ£Œ μ‹œ ended_at μžλ™ μ—…λ°μ΄νŠΈ ν•¨μˆ˜
CREATE FUNCTION update_session_ended_at()
RETURNS TRIGGER AS $$
BEGIN
    IF NEW.status = 'completed' OR NEW.status = 'archived' THEN
        IF NEW.ended_at IS NULL THEN
            NEW.ended_at = NOW();
        END IF;
    END IF;
    NEW.updated_at = NOW();
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- μžλ™ μ„Έμ…˜ 생성 ν•¨μˆ˜ (chat_history insert μ‹œ)
CREATE FUNCTION auto_create_session()
RETURNS TRIGGER AS $$
BEGIN
    -- session_idκ°€ sessions에 μ—†μœΌλ©΄ μžλ™ 생성
    IF NOT EXISTS (SELECT 1 FROM sessions WHERE session_id = NEW.session_id) THEN
        INSERT INTO sessions (session_id, student_id, scenario_id, status)
        VALUES (NEW.session_id, NEW.student_id, NEW.scenario_id, 'active')
        ON CONFLICT (session_id) DO NOTHING;
    END IF;
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- 트리거 생성
CREATE TRIGGER update_patients_updated_at 
    BEFORE UPDATE ON patients
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_scenarios_updated_at 
    BEFORE UPDATE ON scenarios
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_sessions_updated_at
    BEFORE UPDATE ON sessions
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

CREATE TRIGGER update_session_on_status_change
    BEFORE UPDATE ON sessions
    FOR EACH ROW
    WHEN (OLD.status IS DISTINCT FROM NEW.status)
    EXECUTE FUNCTION update_session_ended_at();

CREATE TRIGGER update_sbar_drafts_updated_at 
    BEFORE UPDATE ON sbar_drafts
    FOR EACH ROW
    EXECUTE FUNCTION update_updated_at_column();

-- chat_history insert μ‹œ sessions μžλ™ 생성
CREATE TRIGGER auto_create_session_trigger
    BEFORE INSERT ON chat_history
    FOR EACH ROW
    EXECUTE FUNCTION auto_create_session();

-- =====================================================
-- 4. Row Level Security (RLS) μ„€μ •
-- =====================================================

-- RLS ν™œμ„±ν™”
ALTER TABLE patients ENABLE ROW LEVEL SECURITY;
ALTER TABLE scenarios ENABLE ROW LEVEL SECURITY;
ALTER TABLE sessions ENABLE ROW LEVEL SECURITY;
ALTER TABLE chat_history ENABLE ROW LEVEL SECURITY;
ALTER TABLE handoff_records ENABLE ROW LEVEL SECURITY;
ALTER TABLE sbar_drafts ENABLE ROW LEVEL SECURITY;

-- μ •μ±… 생성 (λͺ¨λ“  μ‚¬μš©μž μ ‘κ·Ό ν—ˆμš©)
CREATE POLICY "patients_all_access" ON patients 
    FOR ALL USING (true) WITH CHECK (true);

CREATE POLICY "scenarios_all_access" ON scenarios 
    FOR ALL USING (true) WITH CHECK (true);

CREATE POLICY "sessions_all_access" ON sessions 
    FOR ALL USING (true) WITH CHECK (true);

CREATE POLICY "chat_history_all_access" ON chat_history 
    FOR ALL USING (true) WITH CHECK (true);

CREATE POLICY "handoff_records_all_access" ON handoff_records 
    FOR ALL USING (true) WITH CHECK (true);

CREATE POLICY "sbar_drafts_all_access" ON sbar_drafts 
    FOR ALL USING (true) WITH CHECK (true);

-- =====================================================
-- μ™„λ£Œ λ©”μ‹œμ§€
-- =====================================================

DO $$
BEGIN
    RAISE NOTICE '';
    RAISE NOTICE '╔══════════════════════════════════════════════════════════════╗';
    RAISE NOTICE 'β•‘  βœ… Supabase μ™„μ „ μ΄ˆκΈ°ν™” μ™„λ£Œ!                                β•‘';
    RAISE NOTICE 'β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•';
    RAISE NOTICE '';
    RAISE NOTICE 'πŸ“Š μƒμ„±λœ ν…Œμ΄λΈ”:';
    RAISE NOTICE '   1. patients (ν™˜μž 정보)';
    RAISE NOTICE '   2. scenarios (μ‹œλ‚˜λ¦¬μ˜€)';
    RAISE NOTICE '   3. sessions (μ„Έμ…˜ 관리 - session_idκ°€ PK)';
    RAISE NOTICE '   4. chat_history (μ±„νŒ… 기둝)';
    RAISE NOTICE '   5. handoff_records (μΈμˆ˜μΈκ³„ 기둝)';
    RAISE NOTICE '   6. sbar_drafts (SBAR μ΄ˆμ•ˆ)';
    RAISE NOTICE '';
    RAISE NOTICE 'πŸ”— μ™Έλž˜ ν‚€:';
    RAISE NOTICE '   - scenarios β†’ patients';
    RAISE NOTICE '   - sessions β†’ scenarios';
    RAISE NOTICE '   - chat_history β†’ sessions (μžλ™ 생성 트리거)';
    RAISE NOTICE '   - handoff_records β†’ sessions, scenarios';
    RAISE NOTICE '   - sbar_drafts β†’ sessions';
    RAISE NOTICE '';
    RAISE NOTICE '⚑ μžλ™ κΈ°λŠ₯:';
    RAISE NOTICE '   - chat_history insert μ‹œ sessions μžλ™ 생성';
    RAISE NOTICE '   - μ„Έμ…˜ μ™„λ£Œ μ‹œ ended_at μžλ™ μ„€μ •';
    RAISE NOTICE '   - updated_at μžλ™ κ°±μ‹ ';
    RAISE NOTICE '';
    RAISE NOTICE 'πŸ“‹ λ‹€μŒ 단계:';
    RAISE NOTICE '   uv run python init_db.py - μ‹œλ‚˜λ¦¬μ˜€ 데이터 λ‘œλ“œ';
    RAISE NOTICE '';
END $$;