LearningnRunning commited on
Commit
2f230ab
Β·
1 Parent(s): 72123d0

feat: Add SQL scripts for complete database reset and initialization, including table and function definitions for sessions, chat history, handoff records, and sbar drafts. Implement foreign key constraints and triggers for automatic session management and updated timestamps.

Browse files
Files changed (2) hide show
  1. remove_strict_fk.sql +16 -0
  2. supabase_init.sql +149 -157
remove_strict_fk.sql ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ -- =====================================================
2
+ -- κΈ°μ‘΄ ν…Œμ΄λΈ” 및 μ œμ•½μ‘°κ±΄ λͺ¨λ‘ μ‚­μ œ (μƒˆλ‘œ μ‹œμž‘)
3
+ -- =====================================================
4
+
5
+ -- μˆœμ„œλŒ€λ‘œ CASCADE μ‚­μ œ
6
+ DROP TABLE IF EXISTS sessions CASCADE;
7
+ DROP TABLE IF EXISTS sbar_drafts CASCADE;
8
+ DROP TABLE IF EXISTS chat_history CASCADE;
9
+ DROP TABLE IF EXISTS handoff_records CASCADE;
10
+ DROP TABLE IF EXISTS scenarios CASCADE;
11
+ DROP TABLE IF EXISTS patients CASCADE;
12
+
13
+ -- ν•¨μˆ˜λ„ μ‚­μ œ (λ‚˜μ€‘μ— μž¬μƒμ„±)
14
+ DROP FUNCTION IF EXISTS update_updated_at_column() CASCADE;
15
+ DROP FUNCTION IF EXISTS update_session_ended_at() CASCADE;
16
+ DROP FUNCTION IF EXISTS auto_create_session() CASCADE;
supabase_init.sql CHANGED
@@ -1,15 +1,16 @@
1
  -- =====================================================
2
- -- Supabase 초기 μ„€μ • 톡합 슀크립트
3
  -- κ°„ν˜Έ μΈμˆ˜μΈκ³„ ꡐ윑 ν”Œλž«νΌ
4
  -- μ‹€ν–‰ μˆœμ„œ: 이 νŒŒμΌμ„ Supabase SQL Editorμ—μ„œ ν•œ 번만 μ‹€ν–‰ν•˜μ„Έμš”
5
  -- =====================================================
6
 
 
7
  -- =====================================================
8
- -- 1. κΈ°λ³Έ ν…Œμ΄λΈ” 생성
9
  -- =====================================================
10
 
11
  -- 1.1 patients ν…Œμ΄λΈ” (ν™˜μž κΈ°λ³Έ 정보)
12
- CREATE TABLE IF NOT EXISTS patients (
13
  id VARCHAR(50) PRIMARY KEY,
14
  name VARCHAR(100) NOT NULL,
15
  age INTEGER NOT NULL,
@@ -27,18 +28,11 @@ CREATE TABLE IF NOT EXISTS patients (
27
  );
28
 
29
  COMMENT ON TABLE patients IS 'ν™˜μž κΈ°λ³Έ 정보';
30
- COMMENT ON COLUMN patients.id IS 'ν™˜μž ID (예: P001)';
31
- COMMENT ON COLUMN patients.name IS 'ν™˜μž 이름';
32
- COMMENT ON COLUMN patients.age IS 'λ‚˜μ΄';
33
- COMMENT ON COLUMN patients.gender IS '성별 (남/μ—¬)';
34
- COMMENT ON COLUMN patients.diagnosis IS '진단λͺ…';
35
- COMMENT ON COLUMN patients.admission_date IS 'μž…μ›μΌ';
36
- COMMENT ON COLUMN patients.allergies IS 'μ•Œλ ˆλ₯΄κΈ°';
37
 
38
  -- 1.2 scenarios ν…Œμ΄λΈ” (μΈμˆ˜μΈκ³„ μ‹œλ‚˜λ¦¬μ˜€)
39
- CREATE TABLE IF NOT EXISTS scenarios (
40
  id VARCHAR(50) PRIMARY KEY,
41
- patient_id VARCHAR(50) NOT NULL REFERENCES patients(id) ON DELETE CASCADE,
42
  title VARCHAR(200) NOT NULL,
43
  day INTEGER NOT NULL,
44
  handoff_situation VARCHAR(200),
@@ -53,23 +47,60 @@ CREATE TABLE IF NOT EXISTS scenarios (
53
  hospital_day INTEGER,
54
  status_badge VARCHAR(20),
55
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
56
- updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
 
 
 
 
 
57
  );
58
 
59
  COMMENT ON TABLE scenarios IS 'μΈμˆ˜μΈκ³„ μ‹œλ‚˜λ¦¬μ˜€';
60
- COMMENT ON COLUMN scenarios.id IS 'μ‹œλ‚˜λ¦¬μ˜€ ID (예: S001_D0_ER_WARD)';
61
- COMMENT ON COLUMN scenarios.patient_id IS 'ν™˜μž ID (μ™Έλž˜ν‚€)';
62
- COMMENT ON COLUMN scenarios.title IS 'μ‹œλ‚˜λ¦¬μ˜€ 제λͺ©';
63
- COMMENT ON COLUMN scenarios.day IS 'μž¬μ› 일수 (0, 1, 2...)';
64
- COMMENT ON COLUMN scenarios.handoff_situation IS 'μΈμˆ˜μΈκ³„ 상황 (예: 응급싀→병동)';
65
-
66
- -- 1.3 handoff_records ν…Œμ΄λΈ” (μΈμˆ˜μΈκ³„ 기둝 및 평가)
67
- -- λŒ€ν™” 기반 평가λ₯Ό κ³ λ €ν•˜μ—¬ NULL ν—ˆμš©
68
- CREATE TABLE IF NOT EXISTS handoff_records (
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
69
  id SERIAL PRIMARY KEY,
70
  session_id VARCHAR(100),
71
  student_id VARCHAR(100) NOT NULL,
72
- scenario_id VARCHAR(50) NOT NULL REFERENCES scenarios(id) ON DELETE CASCADE,
73
  submitted_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
74
  situation TEXT,
75
  background TEXT,
@@ -80,33 +111,25 @@ CREATE TABLE IF NOT EXISTS handoff_records (
80
  strengths JSONB,
81
  improvements JSONB,
82
  detailed_feedback TEXT,
83
- -- λŒ€ν™” 기반 ν‰κ°€μš© 컬럼
84
  missing_critical_info JSONB,
85
- safety_concerns JSONB
 
 
 
 
 
 
 
 
 
 
86
  );
87
 
88
  COMMENT ON TABLE handoff_records IS 'ν•™μƒμ˜ μΈμˆ˜μΈκ³„ 기둝 및 AI 평가 κ²°κ³Ό';
89
- COMMENT ON COLUMN handoff_records.session_id IS 'μ±„νŒ… μ„Έμ…˜ ID';
90
- COMMENT ON COLUMN handoff_records.student_id IS '학생 μ‹λ³„μž';
91
- COMMENT ON COLUMN handoff_records.scenario_id IS 'μ‹œλ‚˜λ¦¬μ˜€ ID (μ™Έλž˜ν‚€)';
92
- COMMENT ON COLUMN handoff_records.missing_critical_info IS 'λˆ„λ½λœ μ€‘μš” 정보 λͺ©λ‘';
93
- COMMENT ON COLUMN handoff_records.safety_concerns IS 'ν™˜μž μ•ˆμ „ κ΄€λ ¨ μ£Όμ˜μ‚¬ν•­ λͺ©λ‘';
94
 
95
- -- 1.4 chat_history ν…Œμ΄λΈ” (학생과 AI κ°„μ˜ μ±„νŒ… 기둝)
96
- CREATE TABLE IF NOT EXISTS chat_history (
97
- id SERIAL PRIMARY KEY,
98
- session_id VARCHAR(100) NOT NULL,
99
- student_id VARCHAR(100) NOT NULL,
100
- scenario_id VARCHAR(50) NOT NULL,
101
- role VARCHAR(20) NOT NULL CHECK (role IN ('user', 'assistant')),
102
- message TEXT NOT NULL,
103
- timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW()
104
- );
105
-
106
- COMMENT ON TABLE chat_history IS '학생과 AI κ°„μ˜ μ±„νŒ… 기둝';
107
-
108
- -- 1.5 sbar_drafts ν…Œμ΄λΈ” (μž‘μ„± 쀑인 SBAR μž„μ‹œ μ €μž₯)
109
- CREATE TABLE IF NOT EXISTS sbar_drafts (
110
  id SERIAL PRIMARY KEY,
111
  session_id VARCHAR(100) NOT NULL UNIQUE,
112
  student_id VARCHAR(100) NOT NULL,
@@ -115,63 +138,54 @@ CREATE TABLE IF NOT EXISTS sbar_drafts (
115
  background TEXT DEFAULT '',
116
  assessment TEXT DEFAULT '',
117
  recommendation TEXT DEFAULT '',
118
- updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
 
 
 
 
 
119
  );
120
 
121
  COMMENT ON TABLE sbar_drafts IS 'μž‘μ„± 쀑인 SBAR μž„μ‹œ μ €μž₯';
122
 
123
- -- 1.6 sessions ν…Œμ΄λΈ” (μ„Έμ…˜ 관리)
124
- CREATE TABLE IF NOT EXISTS sessions (
125
- id SERIAL PRIMARY KEY,
126
- session_id VARCHAR(100) UNIQUE NOT NULL,
127
- student_id VARCHAR(100) NOT NULL,
128
- scenario_id VARCHAR(50) NOT NULL,
129
- status VARCHAR(20) DEFAULT 'active',
130
- started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
131
- ended_at TIMESTAMP WITH TIME ZONE,
132
- created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
133
- updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW()
134
- );
135
-
136
- COMMENT ON TABLE sessions IS 'TSID 기반 μ±„νŒ… μ„Έμ…˜ 관리 ν…Œμ΄λΈ”';
137
- COMMENT ON COLUMN sessions.session_id IS 'TSID 기반 μ„Έμ…˜ μ‹λ³„μž (ses_XXXXX ν˜•μ‹)';
138
- COMMENT ON COLUMN sessions.status IS 'μ„Έμ…˜ μƒνƒœ: active, completed, archived';
139
-
140
  -- =====================================================
141
  -- 2. 인덱슀 생성 (μ„±λŠ₯ μ΅œμ ν™”)
142
  -- =====================================================
143
 
144
- -- handoff_records 인덱슀
145
- CREATE INDEX IF NOT EXISTS idx_handoff_records_student ON handoff_records(student_id);
146
- CREATE INDEX IF NOT EXISTS idx_handoff_records_scenario ON handoff_records(scenario_id);
147
- CREATE INDEX IF NOT EXISTS idx_handoff_records_session ON handoff_records(session_id);
148
- CREATE INDEX IF NOT EXISTS idx_handoff_records_submitted ON handoff_records(submitted_at DESC);
 
149
 
150
- -- chat_history 인덱슀
151
- CREATE INDEX IF NOT EXISTS idx_chat_history_session ON chat_history(session_id);
152
- CREATE INDEX IF NOT EXISTS idx_chat_history_student ON chat_history(student_id);
153
- CREATE INDEX IF NOT EXISTS idx_chat_history_timestamp ON chat_history(timestamp);
 
154
 
155
- -- sbar_drafts 인덱슀
156
- CREATE INDEX IF NOT EXISTS idx_sbar_drafts_session ON sbar_drafts(session_id);
157
- CREATE INDEX IF NOT EXISTS idx_sbar_drafts_student ON sbar_drafts(student_id);
 
158
 
159
- -- scenarios 인덱슀
160
- CREATE INDEX IF NOT EXISTS idx_scenarios_patient ON scenarios(patient_id);
161
- CREATE INDEX IF NOT EXISTS idx_scenarios_day ON scenarios(day);
 
 
162
 
163
- -- sessions 인덱슀
164
- CREATE INDEX IF NOT EXISTS idx_sessions_student_id ON sessions(student_id);
165
- CREATE INDEX IF NOT EXISTS idx_sessions_scenario_id ON sessions(scenario_id);
166
- CREATE INDEX IF NOT EXISTS idx_sessions_status ON sessions(status);
167
- CREATE INDEX IF NOT EXISTS idx_sessions_started_at ON sessions(started_at DESC);
168
 
169
  -- =====================================================
170
  -- 3. 트리거 ν•¨μˆ˜ 및 트리거 생성
171
  -- =====================================================
172
 
173
  -- updated_at μžλ™ μ—…λ°μ΄νŠΈ ν•¨μˆ˜
174
- CREATE OR REPLACE FUNCTION update_updated_at_column()
175
  RETURNS TRIGGER AS $$
176
  BEGIN
177
  NEW.updated_at = NOW();
@@ -180,7 +194,7 @@ END;
180
  $$ LANGUAGE plpgsql;
181
 
182
  -- μ„Έμ…˜ μ’…λ£Œ μ‹œ ended_at μžλ™ μ—…λ°μ΄νŠΈ ν•¨μˆ˜
183
- CREATE OR REPLACE FUNCTION update_session_ended_at()
184
  RETURNS TRIGGER AS $$
185
  BEGIN
186
  IF NEW.status = 'completed' OR NEW.status = 'archived' THEN
@@ -193,6 +207,20 @@ BEGIN
193
  END;
194
  $$ LANGUAGE plpgsql;
195
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
  -- 트리거 생성
197
  CREATE TRIGGER update_patients_updated_at
198
  BEFORE UPDATE ON patients
@@ -204,11 +232,6 @@ CREATE TRIGGER update_scenarios_updated_at
204
  FOR EACH ROW
205
  EXECUTE FUNCTION update_updated_at_column();
206
 
207
- CREATE TRIGGER update_sbar_drafts_updated_at
208
- BEFORE UPDATE ON sbar_drafts
209
- FOR EACH ROW
210
- EXECUTE FUNCTION update_updated_at_column();
211
-
212
  CREATE TRIGGER update_sessions_updated_at
213
  BEFORE UPDATE ON sessions
214
  FOR EACH ROW
@@ -220,6 +243,17 @@ CREATE TRIGGER update_session_on_status_change
220
  WHEN (OLD.status IS DISTINCT FROM NEW.status)
221
  EXECUTE FUNCTION update_session_ended_at();
222
 
 
 
 
 
 
 
 
 
 
 
 
223
  -- =====================================================
224
  -- 4. Row Level Security (RLS) μ„€μ •
225
  -- =====================================================
@@ -227,76 +261,28 @@ CREATE TRIGGER update_session_on_status_change
227
  -- RLS ν™œμ„±ν™”
228
  ALTER TABLE patients ENABLE ROW LEVEL SECURITY;
229
  ALTER TABLE scenarios ENABLE ROW LEVEL SECURITY;
230
- ALTER TABLE handoff_records ENABLE ROW LEVEL SECURITY;
231
  ALTER TABLE chat_history ENABLE ROW LEVEL SECURITY;
 
232
  ALTER TABLE sbar_drafts ENABLE ROW LEVEL SECURITY;
233
- ALTER TABLE sessions ENABLE ROW LEVEL SECURITY;
234
 
235
- -- κΈ°μ‘΄ μ •μ±… μ‚­μ œ (쀑볡 λ°©μ§€)
236
- DO $$
237
- BEGIN
238
- -- Chat History μ •μ±… μ‚­μ œ
239
- DROP POLICY IF EXISTS "Allow all for chat_history" ON chat_history;
240
- DROP POLICY IF EXISTS "Allow all operations for anon users on chat_history" ON chat_history;
241
- DROP POLICY IF EXISTS "Allow all operations for authenticated users on chat_history" ON chat_history;
242
- DROP POLICY IF EXISTS "Enable all access for chat_history" ON chat_history;
243
- DROP POLICY IF EXISTS "chat_history_all_access" ON chat_history;
244
-
245
- -- Handoff Records μ •μ±… μ‚­μ œ
246
- DROP POLICY IF EXISTS "Allow all for handoff_records" ON handoff_records;
247
- DROP POLICY IF EXISTS "Allow all operations for anon users on handoff_records" ON handoff_records;
248
- DROP POLICY IF EXISTS "Allow all operations for authenticated users on handoff_records" ON handoff_records;
249
- DROP POLICY IF EXISTS "Enable all access for handoff_records" ON handoff_records;
250
- DROP POLICY IF EXISTS "handoff_records_all_access" ON handoff_records;
251
-
252
- -- SBAR Drafts μ •μ±… μ‚­μ œ
253
- DROP POLICY IF EXISTS "Allow all for sbar_drafts" ON sbar_drafts;
254
- DROP POLICY IF EXISTS "Allow all operations for anon users on sbar_drafts" ON sbar_drafts;
255
- DROP POLICY IF EXISTS "Allow all operations for authenticated users on sbar_drafts" ON sbar_drafts;
256
- DROP POLICY IF EXISTS "Enable all access for sbar_drafts" ON sbar_drafts;
257
- DROP POLICY IF EXISTS "sbar_drafts_all_access" ON sbar_drafts;
258
-
259
- -- Patients μ •μ±… μ‚­μ œ
260
- DROP POLICY IF EXISTS "Allow read access for all users" ON patients;
261
- DROP POLICY IF EXISTS "Allow all for patients" ON patients;
262
- DROP POLICY IF EXISTS "Allow all operations for anon users on patients" ON patients;
263
- DROP POLICY IF EXISTS "Allow all operations for authenticated users on patients" ON patients;
264
- DROP POLICY IF EXISTS "Enable all operations for anon users" ON patients;
265
- DROP POLICY IF EXISTS "Enable all operations for authenticated users" ON patients;
266
- DROP POLICY IF EXISTS "patients_all_access" ON patients;
267
-
268
- -- Scenarios μ •μ±… μ‚­μ œ
269
- DROP POLICY IF EXISTS "Allow read access for all users" ON scenarios;
270
- DROP POLICY IF EXISTS "Allow all for scenarios" ON scenarios;
271
- DROP POLICY IF EXISTS "Allow all operations for anon users on scenarios" ON scenarios;
272
- DROP POLICY IF EXISTS "Allow all operations for authenticated users on scenarios" ON scenarios;
273
- DROP POLICY IF EXISTS "Enable all operations for anon users" ON scenarios;
274
- DROP POLICY IF EXISTS "Enable all operations for authenticated users" ON scenarios;
275
- DROP POLICY IF EXISTS "scenarios_all_access" ON scenarios;
276
-
277
- -- Sessions μ •μ±… μ‚­μ œ
278
- DROP POLICY IF EXISTS "Enable all operations for anon users" ON sessions;
279
- DROP POLICY IF EXISTS "Enable all operations for authenticated users" ON sessions;
280
- DROP POLICY IF EXISTS "sessions_all_access" ON sessions;
281
- END $$;
282
-
283
- -- μƒˆ μ •μ±… 생성 (λͺ¨λ“  μ‚¬μš©μž μ ‘κ·Ό ν—ˆμš©)
284
  CREATE POLICY "patients_all_access" ON patients
285
  FOR ALL USING (true) WITH CHECK (true);
286
 
287
  CREATE POLICY "scenarios_all_access" ON scenarios
288
  FOR ALL USING (true) WITH CHECK (true);
289
 
290
- CREATE POLICY "handoff_records_all_access" ON handoff_records
291
  FOR ALL USING (true) WITH CHECK (true);
292
 
293
  CREATE POLICY "chat_history_all_access" ON chat_history
294
  FOR ALL USING (true) WITH CHECK (true);
295
 
296
- CREATE POLICY "sbar_drafts_all_access" ON sbar_drafts
297
  FOR ALL USING (true) WITH CHECK (true);
298
 
299
- CREATE POLICY "sessions_all_access" ON sessions
300
  FOR ALL USING (true) WITH CHECK (true);
301
 
302
  -- =====================================================
@@ -307,25 +293,31 @@ DO $$
307
  BEGIN
308
  RAISE NOTICE '';
309
  RAISE NOTICE '╔══════════════════════════════════════════════════════════════╗';
310
- RAISE NOTICE 'β•‘ βœ… Supabase 초기 μ„€μ • μ™„λ£Œ! β•‘';
311
  RAISE NOTICE 'β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•';
312
  RAISE NOTICE '';
313
  RAISE NOTICE 'πŸ“Š μƒμ„±λœ ν…Œμ΄λΈ”:';
314
- RAISE NOTICE ' - patients (ν™˜μž 정보)';
315
- RAISE NOTICE ' - scenarios (μ‹œλ‚˜λ¦¬μ˜€)';
316
- RAISE NOTICE ' - handoff_records (μΈμˆ˜μΈκ³„ 기둝)';
317
- RAISE NOTICE ' - chat_history (μ±„νŒ… 기둝)';
318
- RAISE NOTICE ' - sbar_drafts (SBAR μ΄ˆμ•ˆ)';
319
- RAISE NOTICE ' - sessions (μ„Έμ…˜ 관리)';
 
 
 
 
 
 
 
320
  RAISE NOTICE '';
321
- RAISE NOTICE 'πŸ”’ RLS μ •μ±…: λͺ¨λ“  ν…Œμ΄λΈ”μ— 전체 μ ‘κ·Ό ν—ˆμš©';
322
- RAISE NOTICE 'πŸ“ˆ 인덱슀: μ„±λŠ₯ μ΅œμ ν™” μ™„λ£Œ';
323
- RAISE NOTICE 'βš™οΈ 트리거: μžλ™ updated_at μ—…λ°μ΄νŠΈ 및 μ„Έμ…˜ μ’…λ£Œ 처리';
 
324
  RAISE NOTICE '';
325
  RAISE NOTICE 'πŸ“‹ λ‹€μŒ 단계:';
326
- RAISE NOTICE ' 1. uv run scripts/init_db.py - μ‹œλ‚˜λ¦¬μ˜€ 데이터 λ‘œλ“œ';
327
- RAISE NOTICE ' 2. uv run test_supabase_connection.py - μ—°κ²° ν…ŒμŠ€νŠΈ';
328
- RAISE NOTICE ' 3. uv run app.py - μ•± μ‹€ν–‰';
329
  RAISE NOTICE '';
330
  END $$;
331
 
 
1
  -- =====================================================
2
+ -- Supabase 초기 μ„€μ • 톡합 슀크립트 (μ™„μ „ μ΄ˆκΈ°ν™” 버전)
3
  -- κ°„ν˜Έ μΈμˆ˜μΈκ³„ ꡐ윑 ν”Œλž«νΌ
4
  -- μ‹€ν–‰ μˆœμ„œ: 이 νŒŒμΌμ„ Supabase SQL Editorμ—μ„œ ν•œ 번만 μ‹€ν–‰ν•˜μ„Έμš”
5
  -- =====================================================
6
 
7
+
8
  -- =====================================================
9
+ -- 1. κΈ°λ³Έ ν…Œμ΄λΈ” 생성 (μ˜μ‘΄μ„± μˆœμ„œλŒ€λ‘œ)
10
  -- =====================================================
11
 
12
  -- 1.1 patients ν…Œμ΄λΈ” (ν™˜μž κΈ°λ³Έ 정보)
13
+ CREATE TABLE patients (
14
  id VARCHAR(50) PRIMARY KEY,
15
  name VARCHAR(100) NOT NULL,
16
  age INTEGER NOT NULL,
 
28
  );
29
 
30
  COMMENT ON TABLE patients IS 'ν™˜μž κΈ°λ³Έ 정보';
 
 
 
 
 
 
 
31
 
32
  -- 1.2 scenarios ν…Œμ΄λΈ” (μΈμˆ˜μΈκ³„ μ‹œλ‚˜λ¦¬μ˜€)
33
+ CREATE TABLE scenarios (
34
  id VARCHAR(50) PRIMARY KEY,
35
+ patient_id VARCHAR(50) NOT NULL,
36
  title VARCHAR(200) NOT NULL,
37
  day INTEGER NOT NULL,
38
  handoff_situation VARCHAR(200),
 
47
  hospital_day INTEGER,
48
  status_badge VARCHAR(20),
49
  created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
50
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
51
+ CONSTRAINT scenarios_patient_fkey
52
+ FOREIGN KEY (patient_id)
53
+ REFERENCES patients(id)
54
+ ON DELETE CASCADE
55
+ ON UPDATE CASCADE
56
  );
57
 
58
  COMMENT ON TABLE scenarios IS 'μΈμˆ˜μΈκ³„ μ‹œλ‚˜λ¦¬μ˜€';
59
+
60
+ -- 1.3 sessions ν…Œμ΄λΈ” (μ„Έμ…˜ 관리) - session_idλ₯Ό PK둜
61
+ CREATE TABLE sessions (
62
+ session_id VARCHAR(100) PRIMARY KEY,
63
+ student_id VARCHAR(100) NOT NULL,
64
+ scenario_id VARCHAR(50) NOT NULL,
65
+ status VARCHAR(20) DEFAULT 'active',
66
+ started_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
67
+ ended_at TIMESTAMP WITH TIME ZONE,
68
+ created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
69
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
70
+ CONSTRAINT sessions_scenario_fkey
71
+ FOREIGN KEY (scenario_id)
72
+ REFERENCES scenarios(id)
73
+ ON DELETE RESTRICT
74
+ ON UPDATE CASCADE
75
+ );
76
+
77
+ COMMENT ON TABLE sessions IS 'TSID 기반 μ±„νŒ… μ„Έμ…˜ 관리 ν…Œμ΄λΈ”';
78
+ COMMENT ON COLUMN sessions.session_id IS 'TSID 기반 μ„Έμ…˜ μ‹λ³„μž (PRIMARY KEY)';
79
+
80
+ -- 1.4 chat_history ν…Œμ΄λΈ” (학생과 AI κ°„μ˜ μ±„νŒ… 기둝)
81
+ CREATE TABLE chat_history (
82
+ id SERIAL PRIMARY KEY,
83
+ session_id VARCHAR(100) NOT NULL,
84
+ student_id VARCHAR(100) NOT NULL,
85
+ scenario_id VARCHAR(50) NOT NULL,
86
+ role VARCHAR(20) NOT NULL CHECK (role IN ('user', 'assistant')),
87
+ message TEXT NOT NULL,
88
+ timestamp TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
89
+ CONSTRAINT chat_history_session_fkey
90
+ FOREIGN KEY (session_id)
91
+ REFERENCES sessions(session_id)
92
+ ON DELETE CASCADE
93
+ ON UPDATE CASCADE
94
+ );
95
+
96
+ COMMENT ON TABLE chat_history IS '학생과 AI κ°„μ˜ μ±„νŒ… 기둝';
97
+
98
+ -- 1.5 handoff_records ν…Œμ΄λΈ” (μΈμˆ˜μΈκ³„ 기둝 및 평가)
99
+ CREATE TABLE handoff_records (
100
  id SERIAL PRIMARY KEY,
101
  session_id VARCHAR(100),
102
  student_id VARCHAR(100) NOT NULL,
103
+ scenario_id VARCHAR(50) NOT NULL,
104
  submitted_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
105
  situation TEXT,
106
  background TEXT,
 
111
  strengths JSONB,
112
  improvements JSONB,
113
  detailed_feedback TEXT,
 
114
  missing_critical_info JSONB,
115
+ safety_concerns JSONB,
116
+ CONSTRAINT handoff_records_scenario_fkey
117
+ FOREIGN KEY (scenario_id)
118
+ REFERENCES scenarios(id)
119
+ ON DELETE RESTRICT
120
+ ON UPDATE CASCADE,
121
+ CONSTRAINT handoff_records_session_fkey
122
+ FOREIGN KEY (session_id)
123
+ REFERENCES sessions(session_id)
124
+ ON DELETE SET NULL
125
+ ON UPDATE CASCADE
126
  );
127
 
128
  COMMENT ON TABLE handoff_records IS 'ν•™μƒμ˜ μΈμˆ˜μΈκ³„ 기둝 및 AI 평가 κ²°κ³Ό';
129
+ COMMENT ON COLUMN handoff_records.session_id IS 'μ±„νŒ… μ„Έμ…˜ ID (nullable)';
 
 
 
 
130
 
131
+ -- 1.6 sbar_drafts ν…Œμ΄λΈ” (μž‘μ„± 쀑인 SBAR μž„μ‹œ μ €μž₯)
132
+ CREATE TABLE sbar_drafts (
 
 
 
 
 
 
 
 
 
 
 
 
 
133
  id SERIAL PRIMARY KEY,
134
  session_id VARCHAR(100) NOT NULL UNIQUE,
135
  student_id VARCHAR(100) NOT NULL,
 
138
  background TEXT DEFAULT '',
139
  assessment TEXT DEFAULT '',
140
  recommendation TEXT DEFAULT '',
141
+ updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(),
142
+ CONSTRAINT sbar_drafts_session_fkey
143
+ FOREIGN KEY (session_id)
144
+ REFERENCES sessions(session_id)
145
+ ON DELETE CASCADE
146
+ ON UPDATE CASCADE
147
  );
148
 
149
  COMMENT ON TABLE sbar_drafts IS 'μž‘μ„± 쀑인 SBAR μž„μ‹œ μ €μž₯';
150
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  -- =====================================================
152
  -- 2. 인덱슀 생성 (μ„±λŠ₯ μ΅œμ ν™”)
153
  -- =====================================================
154
 
155
+ -- patients
156
+ CREATE INDEX idx_patients_name ON patients(name);
157
+
158
+ -- scenarios
159
+ CREATE INDEX idx_scenarios_patient ON scenarios(patient_id);
160
+ CREATE INDEX idx_scenarios_day ON scenarios(day);
161
 
162
+ -- sessions
163
+ CREATE INDEX idx_sessions_student ON sessions(student_id);
164
+ CREATE INDEX idx_sessions_scenario ON sessions(scenario_id);
165
+ CREATE INDEX idx_sessions_status ON sessions(status);
166
+ CREATE INDEX idx_sessions_started ON sessions(started_at DESC);
167
 
168
+ -- chat_history
169
+ CREATE INDEX idx_chat_history_session ON chat_history(session_id);
170
+ CREATE INDEX idx_chat_history_student ON chat_history(student_id);
171
+ CREATE INDEX idx_chat_history_timestamp ON chat_history(timestamp);
172
 
173
+ -- handoff_records
174
+ CREATE INDEX idx_handoff_student ON handoff_records(student_id);
175
+ CREATE INDEX idx_handoff_scenario ON handoff_records(scenario_id);
176
+ CREATE INDEX idx_handoff_session ON handoff_records(session_id);
177
+ CREATE INDEX idx_handoff_submitted ON handoff_records(submitted_at DESC);
178
 
179
+ -- sbar_drafts
180
+ CREATE INDEX idx_sbar_session ON sbar_drafts(session_id);
181
+ CREATE INDEX idx_sbar_student ON sbar_drafts(student_id);
 
 
182
 
183
  -- =====================================================
184
  -- 3. 트리거 ν•¨μˆ˜ 및 트리거 생성
185
  -- =====================================================
186
 
187
  -- updated_at μžλ™ μ—…λ°μ΄νŠΈ ν•¨μˆ˜
188
+ CREATE FUNCTION update_updated_at_column()
189
  RETURNS TRIGGER AS $$
190
  BEGIN
191
  NEW.updated_at = NOW();
 
194
  $$ LANGUAGE plpgsql;
195
 
196
  -- μ„Έμ…˜ μ’…λ£Œ μ‹œ ended_at μžλ™ μ—…λ°μ΄νŠΈ ν•¨μˆ˜
197
+ CREATE FUNCTION update_session_ended_at()
198
  RETURNS TRIGGER AS $$
199
  BEGIN
200
  IF NEW.status = 'completed' OR NEW.status = 'archived' THEN
 
207
  END;
208
  $$ LANGUAGE plpgsql;
209
 
210
+ -- μžλ™ μ„Έμ…˜ 생성 ν•¨μˆ˜ (chat_history insert μ‹œ)
211
+ CREATE FUNCTION auto_create_session()
212
+ RETURNS TRIGGER AS $$
213
+ BEGIN
214
+ -- session_idκ°€ sessions에 μ—†μœΌλ©΄ μžλ™ 생성
215
+ IF NOT EXISTS (SELECT 1 FROM sessions WHERE session_id = NEW.session_id) THEN
216
+ INSERT INTO sessions (session_id, student_id, scenario_id, status)
217
+ VALUES (NEW.session_id, NEW.student_id, NEW.scenario_id, 'active')
218
+ ON CONFLICT (session_id) DO NOTHING;
219
+ END IF;
220
+ RETURN NEW;
221
+ END;
222
+ $$ LANGUAGE plpgsql;
223
+
224
  -- 트리거 생성
225
  CREATE TRIGGER update_patients_updated_at
226
  BEFORE UPDATE ON patients
 
232
  FOR EACH ROW
233
  EXECUTE FUNCTION update_updated_at_column();
234
 
 
 
 
 
 
235
  CREATE TRIGGER update_sessions_updated_at
236
  BEFORE UPDATE ON sessions
237
  FOR EACH ROW
 
243
  WHEN (OLD.status IS DISTINCT FROM NEW.status)
244
  EXECUTE FUNCTION update_session_ended_at();
245
 
246
+ CREATE TRIGGER update_sbar_drafts_updated_at
247
+ BEFORE UPDATE ON sbar_drafts
248
+ FOR EACH ROW
249
+ EXECUTE FUNCTION update_updated_at_column();
250
+
251
+ -- chat_history insert μ‹œ sessions μžλ™ 생성
252
+ CREATE TRIGGER auto_create_session_trigger
253
+ BEFORE INSERT ON chat_history
254
+ FOR EACH ROW
255
+ EXECUTE FUNCTION auto_create_session();
256
+
257
  -- =====================================================
258
  -- 4. Row Level Security (RLS) μ„€μ •
259
  -- =====================================================
 
261
  -- RLS ν™œμ„±ν™”
262
  ALTER TABLE patients ENABLE ROW LEVEL SECURITY;
263
  ALTER TABLE scenarios ENABLE ROW LEVEL SECURITY;
264
+ ALTER TABLE sessions ENABLE ROW LEVEL SECURITY;
265
  ALTER TABLE chat_history ENABLE ROW LEVEL SECURITY;
266
+ ALTER TABLE handoff_records ENABLE ROW LEVEL SECURITY;
267
  ALTER TABLE sbar_drafts ENABLE ROW LEVEL SECURITY;
 
268
 
269
+ -- μ •μ±… 생성 (λͺ¨λ“  μ‚¬μš©μž μ ‘κ·Ό ν—ˆμš©)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
270
  CREATE POLICY "patients_all_access" ON patients
271
  FOR ALL USING (true) WITH CHECK (true);
272
 
273
  CREATE POLICY "scenarios_all_access" ON scenarios
274
  FOR ALL USING (true) WITH CHECK (true);
275
 
276
+ CREATE POLICY "sessions_all_access" ON sessions
277
  FOR ALL USING (true) WITH CHECK (true);
278
 
279
  CREATE POLICY "chat_history_all_access" ON chat_history
280
  FOR ALL USING (true) WITH CHECK (true);
281
 
282
+ CREATE POLICY "handoff_records_all_access" ON handoff_records
283
  FOR ALL USING (true) WITH CHECK (true);
284
 
285
+ CREATE POLICY "sbar_drafts_all_access" ON sbar_drafts
286
  FOR ALL USING (true) WITH CHECK (true);
287
 
288
  -- =====================================================
 
293
  BEGIN
294
  RAISE NOTICE '';
295
  RAISE NOTICE '╔══════════════════════════════════════════════════════════════╗';
296
+ RAISE NOTICE 'β•‘ βœ… Supabase μ™„μ „ μ΄ˆκΈ°ν™” μ™„λ£Œ! β•‘';
297
  RAISE NOTICE 'β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•';
298
  RAISE NOTICE '';
299
  RAISE NOTICE 'πŸ“Š μƒμ„±λœ ν…Œμ΄λΈ”:';
300
+ RAISE NOTICE ' 1. patients (ν™˜μž 정보)';
301
+ RAISE NOTICE ' 2. scenarios (μ‹œλ‚˜λ¦¬μ˜€)';
302
+ RAISE NOTICE ' 3. sessions (μ„Έμ…˜ 관리 - session_idκ°€ PK)';
303
+ RAISE NOTICE ' 4. chat_history (μ±„νŒ… 기둝)';
304
+ RAISE NOTICE ' 5. handoff_records (μΈμˆ˜μΈκ³„ 기둝)';
305
+ RAISE NOTICE ' 6. sbar_drafts (SBAR μ΄ˆμ•ˆ)';
306
+ RAISE NOTICE '';
307
+ RAISE NOTICE 'πŸ”— μ™Έλž˜ ν‚€:';
308
+ RAISE NOTICE ' - scenarios β†’ patients';
309
+ RAISE NOTICE ' - sessions β†’ scenarios';
310
+ RAISE NOTICE ' - chat_history β†’ sessions (μžλ™ 생성 트리거)';
311
+ RAISE NOTICE ' - handoff_records β†’ sessions, scenarios';
312
+ RAISE NOTICE ' - sbar_drafts β†’ sessions';
313
  RAISE NOTICE '';
314
+ RAISE NOTICE '⚑ μžλ™ κΈ°λŠ₯:';
315
+ RAISE NOTICE ' - chat_history insert μ‹œ sessions μžλ™ 생성';
316
+ RAISE NOTICE ' - μ„Έμ…˜ μ™„λ£Œ μ‹œ ended_at μžλ™ μ„€μ •';
317
+ RAISE NOTICE ' - updated_at μžλ™ κ°±μ‹ ';
318
  RAISE NOTICE '';
319
  RAISE NOTICE 'πŸ“‹ λ‹€μŒ 단계:';
320
+ RAISE NOTICE ' uv run python init_db.py - μ‹œλ‚˜λ¦¬μ˜€ 데이터 λ‘œλ“œ';
 
 
321
  RAISE NOTICE '';
322
  END $$;
323