dammmmmmmmm commited on
Commit
b45e10b
·
verified ·
1 Parent(s): 5a3abe0

Update backend/ai/database.py

Browse files
Files changed (1) hide show
  1. backend/ai/database.py +120 -102
backend/ai/database.py CHANGED
@@ -1,43 +1,50 @@
1
- # database_debug.py
2
  import sqlite3
3
  import datetime
 
4
  import os
5
- import traceback
6
 
7
- # --- Đường dẫn DB trên Hugging Face Space ---
8
- DB_NAME = os.path.join("/tmp", "vnpt_call_center.db")
9
- print(f"[DB DEBUG] Database path: {DB_NAME}")
10
 
11
- # --- 20 DATA MẪU ---
 
12
  INITIAL_DATA = [
13
- {"customer_id": "CUS_001", "call_id": "CALL_0001", "call_timestamp": "2025-01-20T08:15:00+07:00", "call_duration_seconds": 78, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2350},
14
- {"customer_id": "CUS_002", "call_id": "CALL_0002", "call_timestamp": "2025-01-20T08:35:00+07:00", "call_duration_seconds": 92, "intent": "cancel_package", "sentiment": "neutral", "ai_resolved": True, "upsell_success": True, "csat": 4, "cost_per_call_vnd": 2480},
15
- # ... giữ nguyên 20 record mẫu ...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  ]
17
 
18
  class Database:
19
  def __init__(self):
20
- try:
21
- print("[DB DEBUG] Khởi tạo Database...")
22
- self._init_db()
23
- print("[DB DEBUG] Database khởi tạo xong.")
24
- except Exception as e:
25
- print(f"[DB ERROR] Init DB failed: {e}")
26
- traceback.print_exc()
27
 
28
  def _get_conn(self):
29
- try:
30
- conn = sqlite3.connect(DB_NAME, check_same_thread=False)
31
- print("[DB DEBUG] Kết nối SQLite mở thành công.")
32
- return conn
33
- except Exception as e:
34
- print(f"[DB ERROR] Không thể mở kết nối DB: {e}")
35
- traceback.print_exc()
36
- raise
37
 
38
  def _init_db(self):
39
  conn = self._get_conn()
40
  cursor = conn.cursor()
 
 
41
  cursor.execute('''
42
  CREATE TABLE IF NOT EXISTS calls (
43
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -53,104 +60,115 @@ class Database:
53
  cost INTEGER
54
  )
55
  ''')
56
- conn.commit()
 
57
  cursor.execute("SELECT count(*) FROM calls")
58
  count = cursor.fetchone()[0]
59
- print(f"[DB DEBUG] Số bản ghi hiện tại: {count}")
60
  if count == 0:
61
- print(" [DB DEBUG] Database trống. Đang nạp 20 bản ghi mẫu...")
62
  self._seed_data(cursor)
63
  conn.commit()
64
- print(" [DB DEBUG] Seed dữ liệu xong.")
65
  conn.close()
66
 
67
  def _seed_data(self, cursor):
68
  for item in INITIAL_DATA:
69
- try:
70
- cursor.execute('''
71
- INSERT INTO calls (customer_id, call_id, call_timestamp, duration, intent, sentiment, ai_resolved, upsell, csat, cost)
72
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
73
- ''', (
74
- item['customer_id'],
75
- item['call_id'],
76
- item['call_timestamp'],
77
- item['call_duration_seconds'],
78
- item['intent'],
79
- item['sentiment'],
80
- item['ai_resolved'],
81
- item['upsell_success'],
82
- item['csat'],
83
- item['cost_per_call_vnd']
84
- ))
85
- except Exception as e:
86
- print(f"[DB ERROR] Seed record {item['call_id']} thất bại: {e}")
87
- traceback.print_exc()
88
-
89
- def get_all_calls(self):
90
- try:
91
- conn = self._get_conn()
92
- cursor = conn.cursor()
93
- cursor.execute("SELECT * FROM calls ORDER BY call_timestamp DESC")
94
- rows = cursor.fetchall()
95
- conn.close()
96
- result = []
97
- for r in rows:
98
- result.append({
99
- "id": r[1], # customer_id
100
- "time": r[3], # timestamp
101
- "dur": r[4], # duration
102
- "intent": r[5],
103
- "sent": r[6],
104
- "ai": bool(r[7]),
105
- "upsell": bool(r[8]),
106
- "csat": r[9],
107
- "cost": r[10]
108
- })
109
- print(f"[DB DEBUG] Lấy {len(result)} bản ghi thành công.")
110
- return result
111
- except Exception as e:
112
- print(f"[DB ERROR] Lấy tất cả calls thất bại: {e}")
113
- traceback.print_exc()
114
- return []
115
-
116
- def add_call(self, customer_id, duration, intent, sentiment, ai_resolved, upsell, cost):
117
- try:
118
- conn = self._get_conn()
119
- cursor = conn.cursor()
120
- call_id = f"LIVE_{int(datetime.datetime.now().timestamp())}"
121
- timestamp = datetime.datetime.now().isoformat()
122
  cursor.execute('''
123
  INSERT INTO calls (customer_id, call_id, call_timestamp, duration, intent, sentiment, ai_resolved, upsell, csat, cost)
124
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
125
- ''', (customer_id, call_id, timestamp, duration, intent, sentiment, ai_resolved, upsell, cost['csat'], cost['value']))
126
- conn.commit()
127
- conn.close()
128
- print(f"[DB DEBUG] Thêm call {call_id} cho customer {customer_id} thành công.")
129
- except Exception as e:
130
- print(f"[DB ERROR] Thêm call thất bại: {e}")
131
- traceback.print_exc()
 
 
 
 
 
132
 
133
- def update_call_rating(self, customer_id, stars):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  try:
135
- conn = self._get_conn()
136
- cursor = conn.cursor()
137
  cursor.execute('''
138
- SELECT id FROM calls
139
- WHERE customer_id = ?
140
- ORDER BY call_timestamp DESC
141
  LIMIT 1
142
  ''', (str(customer_id),))
 
143
  row = cursor.fetchone()
 
144
  if row:
145
- cursor.execute('UPDATE calls SET csat = ? WHERE id = ?', (int(stars), row[0]))
 
 
 
 
 
 
 
 
 
146
  conn.commit()
147
- print(f"[DB DEBUG] Cập nhật CSAT={stars} cho {customer_id}")
148
  else:
149
- print(f"[DB DEBUG] Không tìm thấy call nào để cập nhật CSAT cho {customer_id}")
150
- conn.close()
151
  except Exception as e:
152
- print(f"[DB ERROR] Update Rating: {e}")
153
- traceback.print_exc()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
154
 
155
- # Singleton debug
156
- db = Database()
 
1
+ # database.py
2
  import sqlite3
3
  import datetime
4
+ import json
5
  import os
 
6
 
7
+ DB_NAME = "vnpt_call_center.db"
 
 
8
 
9
+ # --- 20 DATA MẪU (SOURCE OF TRUTH) ---
10
+ # Tôi đã gộp 3 nhóm A, B, C của bạn vào đây
11
  INITIAL_DATA = [
12
+ {"customer_id": "CUS_001", "call_id": "CALL_0001", "call_timestamp": "2025-01-20T08:15:00+07:00", "call_duration_seconds": 78, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2350},
13
+ {"customer_id": "CUS_002", "call_id": "CALL_0002", "call_timestamp": "2025-01-20T08:35:00+07:00", "call_duration_seconds": 92, "intent": "cancel_package", "sentiment": "neutral", "ai_resolved": True, "upsell_success": True, "csat": 4, "cost_per_call_vnd": 2480},
14
+ {"customer_id": "CUS_003", "call_id": "CALL_0003", "call_timestamp": "2025-01-20T09:05:00+07:00", "call_duration_seconds": 110, "intent": "competitor", "sentiment": "negative", "ai_resolved": False, "upsell_success": False, "csat": 3, "cost_per_call_vnd": 2650},
15
+ {"customer_id": "CUS_004", "call_id": "CALL_0004", "call_timestamp": "2025-01-20T09:40:00+07:00", "call_duration_seconds": 70, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2300},
16
+ {"customer_id": "CUS_005", "call_id": "CALL_0005", "call_timestamp": "2025-01-20T10:10:00+07:00", "call_duration_seconds": 88, "intent": "cancel_package", "sentiment": "neutral", "ai_resolved": True, "upsell_success": True, "csat": 4, "cost_per_call_vnd": 2450},
17
+ {"customer_id": "CUS_006", "call_id": "CALL_0006", "call_timestamp": "2025-01-20T10:45:00+07:00", "call_duration_seconds": 80, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2380},
18
+ {"customer_id": "CUS_007", "call_id": "CALL_0007", "call_timestamp": "2025-01-18T14:20:00+07:00", "call_duration_seconds": 95, "intent": "cancel_package", "sentiment": "neutral", "ai_resolved": True, "upsell_success": True, "csat": 4, "cost_per_call_vnd": 2500},
19
+ {"customer_id": "CUS_008", "call_id": "CALL_0008", "call_timestamp": "2025-01-18T16:05:00+07:00", "call_duration_seconds": 72, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2320},
20
+ {"customer_id": "CUS_009", "call_id": "CALL_0009", "call_timestamp": "2025-01-17T10:30:00+07:00", "call_duration_seconds": 105, "intent": "competitor", "sentiment": "negative", "ai_resolved": False, "upsell_success": False, "csat": 3, "cost_per_call_vnd": 2680},
21
+ {"customer_id": "CUS_010", "call_id": "CALL_0010", "call_timestamp": "2025-01-17T15:10:00+07:00", "call_duration_seconds": 85, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2400},
22
+ {"customer_id": "CUS_011", "call_id": "CALL_0011", "call_timestamp": "2025-01-16T09:45:00+07:00", "call_duration_seconds": 90, "intent": "cancel_package", "sentiment": "neutral", "ai_resolved": True, "upsell_success": True, "csat": 4, "cost_per_call_vnd": 2480},
23
+ {"customer_id": "CUS_012", "call_id": "CALL_0012", "call_timestamp": "2025-01-16T11:20:00+07:00", "call_duration_seconds": 76, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2350},
24
+ {"customer_id": "CUS_013", "call_id": "CALL_0013", "call_timestamp": "2025-01-15T14:55:00+07:00", "call_duration_seconds": 108, "intent": "competitor", "sentiment": "negative", "ai_resolved": False, "upsell_success": False, "csat": 3, "cost_per_call_vnd": 2620},
25
+ {"customer_id": "CUS_014", "call_id": "CALL_0014", "call_timestamp": "2025-01-15T16:40:00+07:00", "call_duration_seconds": 82, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2380},
26
+ {"customer_id": "CUS_015", "call_id": "CALL_0015", "call_timestamp": "2025-01-10T10:15:00+07:00", "call_duration_seconds": 88, "intent": "cancel_package", "sentiment": "neutral", "ai_resolved": True, "upsell_success": True, "csat": 4, "cost_per_call_vnd": 2450},
27
+ {"customer_id": "CUS_016", "call_id": "CALL_0016", "call_timestamp": "2025-01-08T14:30:00+07:00", "call_duration_seconds": 74, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2350},
28
+ {"customer_id": "CUS_017", "call_id": "CALL_0017", "call_timestamp": "2025-01-05T09:50:00+07:00", "call_duration_seconds": 112, "intent": "competitor", "sentiment": "negative", "ai_resolved": False, "upsell_success": False, "csat": 3, "cost_per_call_vnd": 2700},
29
+ {"customer_id": "CUS_018", "call_id": "CALL_0018", "call_timestamp": "2025-01-04T15:05:00+07:00", "call_duration_seconds": 80, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2380},
30
+ {"customer_id": "CUS_019", "call_id": "CALL_0019", "call_timestamp": "2025-01-03T11:40:00+07:00", "call_duration_seconds": 96, "intent": "cancel_package", "sentiment": "neutral", "ai_resolved": True, "upsell_success": True, "csat": 4, "cost_per_call_vnd": 2500},
31
+ {"customer_id": "CUS_020", "call_id": "CALL_0020", "call_timestamp": "2024-12-28T16:10:00+07:00", "call_duration_seconds": 85, "intent": "network_issue", "sentiment": "positive", "ai_resolved": True, "upsell_success": False, "csat": 5, "cost_per_call_vnd": 2400}
32
  ]
33
 
34
  class Database:
35
  def __init__(self):
36
+ self.conn = None
37
+ self._init_db()
 
 
 
 
 
38
 
39
  def _get_conn(self):
40
+ # Kết nối tới file SQLite, check_same_thread=False để dùng với FastAPI async
41
+ return sqlite3.connect(DB_NAME, check_same_thread=False)
 
 
 
 
 
 
42
 
43
  def _init_db(self):
44
  conn = self._get_conn()
45
  cursor = conn.cursor()
46
+
47
+ # Tạo bảng nếu chưa có
48
  cursor.execute('''
49
  CREATE TABLE IF NOT EXISTS calls (
50
  id INTEGER PRIMARY KEY AUTOINCREMENT,
 
60
  cost INTEGER
61
  )
62
  ''')
63
+
64
+ # Kiểm tra xem có dữ liệu chưa
65
  cursor.execute("SELECT count(*) FROM calls")
66
  count = cursor.fetchone()[0]
67
+
68
  if count == 0:
69
+ print(" [DB] Database trống. Đang nạp 20 bản ghi mẫu...")
70
  self._seed_data(cursor)
71
  conn.commit()
72
+
73
  conn.close()
74
 
75
  def _seed_data(self, cursor):
76
  for item in INITIAL_DATA:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  cursor.execute('''
78
  INSERT INTO calls (customer_id, call_id, call_timestamp, duration, intent, sentiment, ai_resolved, upsell, csat, cost)
79
  VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
80
+ ''', (
81
+ item['customer_id'],
82
+ item['call_id'],
83
+ item['call_timestamp'],
84
+ item['call_duration_seconds'],
85
+ item['intent'],
86
+ item['sentiment'],
87
+ item['ai_resolved'],
88
+ item['upsell_success'],
89
+ item['csat'],
90
+ item['cost_per_call_vnd']
91
+ ))
92
 
93
+ # --- HÀM THÊM MỚI (Dùng cho Live Call) ---
94
+ def add_call(self, customer_id, duration, intent, sentiment, ai_resolved, upsell, cost):
95
+ conn = self._get_conn()
96
+ cursor = conn.cursor()
97
+
98
+ # Tạo call_id tự động (LIVE_timestamp)
99
+ call_id = f"LIVE_{int(datetime.datetime.now().timestamp())}"
100
+ timestamp = datetime.datetime.now().isoformat()
101
+
102
+ cursor.execute('''
103
+ INSERT INTO calls (customer_id, call_id, call_timestamp, duration, intent, sentiment, ai_resolved, upsell, csat, cost)
104
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
105
+ ''', (customer_id, call_id, timestamp, duration, intent, sentiment, ai_resolved, upsell, cost['csat'], cost['value']))
106
+ # Lưu ý: tham số cost ở đây tôi tách ra dict csat/value ở logic_flow (xem bước 2)
107
+
108
+ conn.commit()
109
+ conn.close()
110
+ # [THÊM MỚI] Hàm cập nhật đánh giá thực tế từ người dùng
111
+ def update_call_rating(self, customer_id, stars, note):
112
+ conn = self._get_conn()
113
+ cursor = conn.cursor()
114
+
115
  try:
116
+ # 1. Tìm ID cuộc gọi mới nhất của khách hàng này
 
117
  cursor.execute('''
118
+ SELECT id FROM calls
119
+ WHERE customer_id = ?
120
+ ORDER BY call_timestamp DESC
121
  LIMIT 1
122
  ''', (str(customer_id),))
123
+
124
  row = cursor.fetchone()
125
+
126
  if row:
127
+ call_id_db = row[0]
128
+ # 2. Cập nhật điểm CSAT và ghi chú (nếu muốn lưu note thì cần thêm cột note vào bảng, tạm thời ta chỉ update CSAT)
129
+ # Lưu ý: Nếu bạn muốn lưu cả 'note', bạn cần ALTER TABLE hoặc tạo lại DB mới có cột 'note'.
130
+ # Ở đây tôi giả định chỉ update CSAT cho đơn giản.
131
+ cursor.execute('''
132
+ UPDATE calls
133
+ SET csat = ?
134
+ WHERE id = ?
135
+ ''', (int(stars), call_id_db))
136
+
137
  conn.commit()
138
+ print(f" [DB] Đã cập nhật CSAT={stars} cho khách {customer_id} (DB ID: {call_id_db})")
139
  else:
140
+ print(f" [DB] Không tìm thấy cuộc gọi nào của khách {customer_id} để update.")
141
+
142
  except Exception as e:
143
+ print(f" [DB Error] Update Rating: {e}")
144
+ finally:
145
+ conn.close()
146
+ # --- HÀM LẤY DỮ LIỆU CHO DASHBOARD ---
147
+ def get_all_calls(self):
148
+ conn = self._get_conn()
149
+ cursor = conn.cursor()
150
+
151
+ # Lấy tất cả, sắp xếp mới nhất lên đầu
152
+ cursor.execute("SELECT * FROM calls ORDER BY call_timestamp DESC")
153
+ rows = cursor.fetchall()
154
+
155
+ # Convert tuple sang list of dict để trả về JSON
156
+ result = []
157
+ for r in rows:
158
+ result.append({
159
+ "id": r[1], # customer_id
160
+ "time": r[3], # timestamp
161
+ "dur": r[4], # duration
162
+ "intent": r[5],
163
+ "sent": r[6],
164
+ "ai": bool(r[7]),
165
+ "upsell": bool(r[8]),
166
+ "csat": r[9],
167
+ "cost": r[10]
168
+ })
169
+
170
+ conn.close()
171
+ return result
172
 
173
+ # Khởi tạo singleton
174
+ db = Database()