Spaces:
Sleeping
Sleeping
| import sqlite3 | |
| import datetime | |
| import json | |
| import os | |
| import random # <--- Thêm thư viện này để random dữ liệu | |
| # --- CẤU HÌNH ĐƯỜNG DẪN TUYỆT ĐỐI --- | |
| # Lấy thư mục chứa file database.py hiện tại | |
| BASE_DIR = os.path.dirname(os.path.abspath(__file__)) | |
| # Tạo đường dẫn file DB nằm ngay tại thư mục này | |
| DB_PATH = os.path.join(BASE_DIR, "vnpt_call_center.db") | |
| print(f"[DB SYSTEM] Database file path: {DB_PATH}") | |
| # --- HÀM SINH DỮ LIỆU MẪU THÔNG MINH (DYNAMIC DATE) --- | |
| def generate_seed_data(): | |
| data = [] | |
| now = datetime.datetime.now() | |
| # Tạo 20 cuộc gọi mẫu | |
| for i in range(20): | |
| # 12 cuộc gọi cho ngày HÔM NAY (Để Dashboard nhảy số ngay) | |
| if i < 12: | |
| minutes_ago = random.randint(5, 700) # Cách đây vài phút đến vài tiếng | |
| call_time = now - datetime.timedelta(minutes=minutes_ago) | |
| else: | |
| # 8 cuộc gọi còn lại cho TUẦN NÀY (Cách đây 1-6 ngày) | |
| days_ago = random.randint(1, 6) | |
| call_time = now - datetime.timedelta(days=days_ago) | |
| timestamp_str = call_time.isoformat() | |
| # Random chỉ số để biểu đồ đẹp | |
| intent = random.choice(["network_issue", "cancel_package", "competitor", "low_data"]) | |
| # Logic giả lập cảm xúc theo intent | |
| if intent == "cancel_package": | |
| sentiment = "negative" | |
| csat = random.randint(1, 3) | |
| elif intent == "competitor": | |
| sentiment = "neutral" | |
| csat = random.randint(3, 4) | |
| else: | |
| sentiment = "positive" | |
| csat = 5 | |
| item = { | |
| "customer_id": f"CUS_{i+1:03d}", | |
| "call_id": f"SEED_{i+1}", | |
| "call_timestamp": timestamp_str, | |
| "call_duration_seconds": random.randint(60, 300), | |
| "intent": intent, | |
| "sentiment": sentiment, | |
| "ai_resolved": random.choice([True, True, False]), # Tỉ lệ True cao hơn | |
| "upsell_success": random.choice([True, False, False]), | |
| "csat": csat, | |
| "cost_per_call_vnd": random.randint(1500, 5000) | |
| } | |
| data.append(item) | |
| return data | |
| class Database: | |
| def __init__(self): | |
| self.conn = None | |
| self._init_db() | |
| def _get_conn(self): | |
| # SỬA LỖI: Dùng DB_PATH tuyệt đối | |
| return sqlite3.connect(DB_PATH, check_same_thread=False) | |
| def _init_db(self): | |
| conn = self._get_conn() | |
| cursor = conn.cursor() | |
| # Tạo bảng nếu chưa có | |
| cursor.execute(''' | |
| CREATE TABLE IF NOT EXISTS calls ( | |
| id INTEGER PRIMARY KEY AUTOINCREMENT, | |
| customer_id TEXT, | |
| call_id TEXT, | |
| call_timestamp DATETIME, | |
| duration INTEGER, | |
| intent TEXT, | |
| sentiment TEXT, | |
| ai_resolved BOOLEAN, | |
| upsell BOOLEAN, | |
| csat INTEGER, | |
| cost INTEGER | |
| ) | |
| ''') | |
| # Kiểm tra dữ liệu | |
| cursor.execute("SELECT count(*) FROM calls") | |
| count = cursor.fetchone()[0] | |
| if count == 0: | |
| print(" [DB] Database trống. Đang nạp 20 bản ghi mẫu cho HÔM NAY...") | |
| # Gọi hàm sinh dữ liệu động thay vì dùng list tĩnh | |
| seed_data = generate_seed_data() | |
| self._seed_data(cursor, seed_data) | |
| conn.commit() | |
| conn.close() | |
| def _seed_data(self, cursor, data): | |
| for item in data: | |
| cursor.execute(''' | |
| INSERT INTO calls (customer_id, call_id, call_timestamp, duration, intent, sentiment, ai_resolved, upsell, csat, cost) | |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) | |
| ''', ( | |
| item['customer_id'], | |
| item['call_id'], | |
| item['call_timestamp'], | |
| item['call_duration_seconds'], | |
| item['intent'], | |
| item['sentiment'], | |
| item['ai_resolved'], | |
| item['upsell_success'], | |
| item['csat'], | |
| item['cost_per_call_vnd'] | |
| )) | |
| # --- HÀM THÊM MỚI (Logic giữ nguyên) --- | |
| def add_call(self, customer_id, duration, intent, sentiment, ai_resolved, upsell, cost): | |
| conn = self._get_conn() | |
| cursor = conn.cursor() | |
| call_id = f"LIVE_{int(datetime.datetime.now().timestamp())}" | |
| timestamp = datetime.datetime.now().isoformat() | |
| # Xử lý cost dictionary | |
| cost_val = cost['value'] if isinstance(cost, dict) else cost | |
| csat_val = cost['csat'] if isinstance(cost, dict) else 4 | |
| cursor.execute(''' | |
| INSERT INTO calls (customer_id, call_id, call_timestamp, duration, intent, sentiment, ai_resolved, upsell, csat, cost) | |
| VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?) | |
| ''', (customer_id, call_id, timestamp, duration, intent, sentiment, ai_resolved, upsell, csat_val, cost_val)) | |
| conn.commit() | |
| conn.close() | |
| # --- HÀM CẬP NHẬT ĐÁNH GIÁ SAO (Logic giữ nguyên) --- | |
| def update_call_rating(self, customer_id, stars, note): | |
| conn = self._get_conn() | |
| cursor = conn.cursor() | |
| try: | |
| cursor.execute(''' | |
| SELECT id FROM calls | |
| WHERE customer_id = ? | |
| ORDER BY id DESC | |
| LIMIT 1 | |
| ''', (str(customer_id),)) | |
| row = cursor.fetchone() | |
| if row: | |
| call_id_db = row[0] | |
| cursor.execute('UPDATE calls SET csat = ? WHERE id = ?', (int(stars), call_id_db)) | |
| conn.commit() | |
| print(f" [DB] Đã cập nhật CSAT={stars} cho khách {customer_id}") | |
| else: | |
| print(f" [DB] Không tìm thấy cuộc gọi nào của khách {customer_id} để update.") | |
| except Exception as e: | |
| print(f" [DB Error] Update Rating: {e}") | |
| finally: | |
| conn.close() | |
| # --- HÀM LẤY DỮ LIỆU CHO DASHBOARD (Sửa key trả về cho khớp Frontend) --- | |
| def get_all_calls(self): | |
| conn = self._get_conn() | |
| cursor = conn.cursor() | |
| cursor.execute("SELECT * FROM calls ORDER BY call_timestamp DESC") | |
| rows = cursor.fetchall() | |
| result = [] | |
| for r in rows: | |
| result.append({ | |
| "id": r[1], # customer_id | |
| "time": r[3], # call_timestamp | |
| "dur": r[4], # duration | |
| "intent": r[5], | |
| "sent": r[6], | |
| "ai": bool(r[7]), | |
| "upsell": bool(r[8]), | |
| "csat": r[9], | |
| "cost": r[10] | |
| }) | |
| conn.close() | |
| return result | |
| # Khởi tạo singleton | |
| db = Database() |