maitrang04 commited on
Commit
b386adb
·
verified ·
1 Parent(s): e177182

Upload database.py

Browse files
Files changed (1) hide show
  1. backend/ai/database.py +174 -0
backend/ai/database.py ADDED
@@ -0,0 +1,174 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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,
51
+ customer_id TEXT,
52
+ call_id TEXT,
53
+ call_timestamp DATETIME,
54
+ duration INTEGER,
55
+ intent TEXT,
56
+ sentiment TEXT,
57
+ ai_resolved BOOLEAN,
58
+ upsell BOOLEAN,
59
+ csat INTEGER,
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()