""" PAAI (Pharmacist Assistant AI) 로깅 모듈 - API 호출/응답 SQLite 저장 - 분석 결과 및 피드백 관리 """ import sqlite3 import json import os from datetime import datetime, timedelta from pathlib import Path # DB 파일 경로 DB_PATH = Path(__file__).parent / 'paai_logs.db' def init_db(): """DB 초기화 (테이블 생성)""" schema_path = Path(__file__).parent / 'paai_logs_schema.sql' conn = sqlite3.connect(str(DB_PATH)) cursor = conn.cursor() with open(schema_path, 'r', encoding='utf-8') as f: schema = f.read() cursor.executescript(schema) conn.commit() conn.close() print(f"PAAI 로그 DB 초기화 완료: {DB_PATH}") def create_log( pre_serial: str = None, patient_code: str = None, patient_name: str = None, disease_code_1: str = None, disease_name_1: str = None, disease_code_2: str = None, disease_name_2: str = None, current_medications: list = None, previous_serial: str = None, previous_medications: list = None, prescription_changes: dict = None, otc_history: dict = None ) -> int: """ PAAI 분석 로그 생성 (초기 상태) Returns: log_id: 생성된 로그 ID """ if not DB_PATH.exists(): init_db() conn = sqlite3.connect(str(DB_PATH)) cursor = conn.cursor() current_medications = current_medications or [] previous_medications = previous_medications or [] otc_history = otc_history or {} # 환자명 마스킹 masked_name = None if patient_name: masked_name = patient_name[0] + '*' * (len(patient_name) - 1) if len(patient_name) > 1 else patient_name cursor.execute(""" INSERT INTO paai_logs ( pre_serial, patient_code, patient_name, disease_code_1, disease_name_1, disease_code_2, disease_name_2, current_medications, current_med_count, previous_serial, previous_medications, prescription_changes, otc_history, otc_visit_count, status ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'pending') """, ( pre_serial, patient_code, masked_name, disease_code_1, disease_name_1, disease_code_2, disease_name_2, json.dumps(current_medications, ensure_ascii=False), len(current_medications), previous_serial, json.dumps(previous_medications, ensure_ascii=False), json.dumps(prescription_changes, ensure_ascii=False) if prescription_changes else None, json.dumps(otc_history, ensure_ascii=False), otc_history.get('visit_count', 0) )) log_id = cursor.lastrowid conn.commit() conn.close() return log_id def update_kims_result( log_id: int, kims_drug_codes: list = None, kims_interactions: list = None, kims_response_time_ms: int = 0 ): """KIMS 상호작용 결과 업데이트""" conn = sqlite3.connect(str(DB_PATH)) cursor = conn.cursor() kims_drug_codes = kims_drug_codes or [] kims_interactions = kims_interactions or [] # 심각한 상호작용 여부 (severity 1 또는 2) has_severe = any( str(i.get('severity', '5')) in ['1', '2'] for i in kims_interactions ) cursor.execute(""" UPDATE paai_logs SET kims_drug_codes = ?, kims_drug_count = ?, kims_interactions = ?, kims_interaction_count = ?, kims_has_severe = ?, kims_response_time_ms = ?, status = 'kims_done' WHERE id = ? """, ( json.dumps(kims_drug_codes, ensure_ascii=False), len(kims_drug_codes), json.dumps(kims_interactions, ensure_ascii=False), len(kims_interactions), 1 if has_severe else 0, kims_response_time_ms, log_id )) conn.commit() conn.close() def update_ai_result( log_id: int, ai_prompt: str = None, ai_model: str = None, ai_response: dict = None, ai_response_time_ms: int = 0, ai_token_count: int = None ): """AI 분석 결과 업데이트""" conn = sqlite3.connect(str(DB_PATH)) cursor = conn.cursor() cursor.execute(""" UPDATE paai_logs SET ai_prompt = ?, ai_model = ?, ai_response = ?, ai_response_time_ms = ?, ai_token_count = ?, status = 'success' WHERE id = ? """, ( ai_prompt, ai_model, json.dumps(ai_response, ensure_ascii=False) if ai_response else None, ai_response_time_ms, ai_token_count, log_id )) conn.commit() conn.close() def update_error(log_id: int, error_message: str): """에러 상태 업데이트""" conn = sqlite3.connect(str(DB_PATH)) cursor = conn.cursor() cursor.execute(""" UPDATE paai_logs SET status = 'error', error_message = ? WHERE id = ? """, (error_message, log_id)) conn.commit() conn.close() def update_feedback(log_id: int, useful: bool, comment: str = None): """피드백 업데이트""" conn = sqlite3.connect(str(DB_PATH)) cursor = conn.cursor() cursor.execute(""" UPDATE paai_logs SET feedback_useful = ?, feedback_comment = ? WHERE id = ? """, (1 if useful else 0, comment, log_id)) conn.commit() conn.close() def get_recent_logs( limit: int = 100, status: str = None, has_severe: bool = None, date: str = None ) -> list: """최근 로그 조회""" if not DB_PATH.exists(): return [] conn = sqlite3.connect(str(DB_PATH)) conn.row_factory = sqlite3.Row cursor = conn.cursor() query = "SELECT * FROM paai_logs WHERE 1=1" params = [] if status: query += " AND status = ?" params.append(status) if has_severe is not None: query += " AND kims_has_severe = ?" params.append(1 if has_severe else 0) if date: query += " AND DATE(created_at) = ?" params.append(date) query += " ORDER BY created_at DESC LIMIT ?" params.append(limit) cursor.execute(query, params) rows = cursor.fetchall() result = [] for row in rows: log = dict(row) # JSON 필드 파싱 for field in ['current_medications', 'previous_medications', 'prescription_changes', 'otc_history', 'kims_drug_codes', 'kims_interactions', 'ai_response']: if log.get(field): try: log[field] = json.loads(log[field]) except: pass result.append(log) conn.close() return result def get_log_detail(log_id: int) -> dict: """로그 상세 조회""" if not DB_PATH.exists(): return None conn = sqlite3.connect(str(DB_PATH)) conn.row_factory = sqlite3.Row cursor = conn.cursor() cursor.execute("SELECT * FROM paai_logs WHERE id = ?", (log_id,)) row = cursor.fetchone() if not row: conn.close() return None log = dict(row) # JSON 필드 파싱 for field in ['current_medications', 'previous_medications', 'prescription_changes', 'otc_history', 'kims_drug_codes', 'kims_interactions', 'ai_response']: if log.get(field): try: log[field] = json.loads(log[field]) except: pass conn.close() return log def get_stats() -> dict: """통계 조회""" if not DB_PATH.exists(): return { 'total': 0, 'today': 0, 'success_rate': 0, 'avg_response_time': 0, 'severe_count': 0 } conn = sqlite3.connect(str(DB_PATH)) cursor = conn.cursor() today = datetime.now().strftime('%Y-%m-%d') # 전체 건수 cursor.execute("SELECT COUNT(*) FROM paai_logs") total = cursor.fetchone()[0] # 오늘 건수 cursor.execute("SELECT COUNT(*) FROM paai_logs WHERE DATE(created_at) = ?", (today,)) today_count = cursor.fetchone()[0] # 성공률 cursor.execute("SELECT COUNT(*) FROM paai_logs WHERE status = 'success'") success_count = cursor.fetchone()[0] success_rate = (success_count / total * 100) if total > 0 else 0 # 평균 응답시간 cursor.execute("SELECT AVG(ai_response_time_ms) FROM paai_logs WHERE ai_response_time_ms > 0") avg_time = cursor.fetchone()[0] or 0 # 심각한 상호작용 건수 (오늘) cursor.execute(""" SELECT COUNT(*) FROM paai_logs WHERE DATE(created_at) = ? AND kims_has_severe = 1 """, (today,)) severe_count = cursor.fetchone()[0] # 피드백 통계 cursor.execute("SELECT COUNT(*) FROM paai_logs WHERE feedback_useful = 1") useful_count = cursor.fetchone()[0] cursor.execute("SELECT COUNT(*) FROM paai_logs WHERE feedback_useful IS NOT NULL") feedback_total = cursor.fetchone()[0] conn.close() return { 'total': total, 'today': today_count, 'success_rate': round(success_rate, 1), 'avg_response_time': int(avg_time), 'severe_count': severe_count, 'feedback': { 'useful': useful_count, 'total': feedback_total, 'rate': round(useful_count / feedback_total * 100, 1) if feedback_total > 0 else 0 } }