feat: KIMS API 호출 SQLite 로깅 (AI 학습용 데이터 수집)
This commit is contained in:
parent
8c20c8b8db
commit
fbe7dde4ce
@ -4140,11 +4140,16 @@ def api_kims_interaction_check():
|
||||
}
|
||||
"""
|
||||
import requests as http_requests
|
||||
from db.kims_logger import log_kims_call
|
||||
import time as time_module
|
||||
|
||||
start_time = time_module.time()
|
||||
|
||||
try:
|
||||
data = request.get_json()
|
||||
drug_codes = data.get('drug_codes', [])
|
||||
pre_serial = data.get('pre_serial', '')
|
||||
user_id = data.get('user_id') # 회원 ID (있으면)
|
||||
|
||||
if len(drug_codes) < 2:
|
||||
return jsonify({
|
||||
@ -4202,6 +4207,10 @@ def api_kims_interaction_check():
|
||||
)
|
||||
|
||||
if kims_response.status_code != 200:
|
||||
log_kims_call(pre_serial=pre_serial, drug_codes=kd_codes, drug_names=[d['name'] for d in drugs_info],
|
||||
api_status='ERROR', http_status=kims_response.status_code,
|
||||
response_time_ms=int((time_module.time() - start_time) * 1000),
|
||||
error_message=f'HTTP {kims_response.status_code}')
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'KIMS API 응답 오류: HTTP {kims_response.status_code}',
|
||||
@ -4211,6 +4220,10 @@ def api_kims_interaction_check():
|
||||
kims_data = kims_response.json()
|
||||
|
||||
if kims_data.get('Message') != 'SUCCESS':
|
||||
log_kims_call(pre_serial=pre_serial, drug_codes=kd_codes, drug_names=[d['name'] for d in drugs_info],
|
||||
api_status='ERROR', http_status=200,
|
||||
response_time_ms=int((time_module.time() - start_time) * 1000),
|
||||
error_message=f'KIMS: {kims_data.get("Message")}', response_raw=kims_data)
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': f'KIMS API 처리 실패: {kims_data.get("Message", "알 수 없는 오류")}',
|
||||
@ -4218,12 +4231,17 @@ def api_kims_interaction_check():
|
||||
}), 502
|
||||
|
||||
except http_requests.Timeout:
|
||||
log_kims_call(pre_serial=pre_serial, drug_codes=kd_codes, drug_names=[d['name'] for d in drugs_info],
|
||||
api_status='TIMEOUT', response_time_ms=10000, error_message='10초 초과')
|
||||
return jsonify({
|
||||
'success': False,
|
||||
'error': 'KIMS API 타임아웃 (10초 초과)',
|
||||
'drugs_checked': drugs_info
|
||||
}), 504
|
||||
except Exception as kims_err:
|
||||
log_kims_call(pre_serial=pre_serial, drug_codes=kd_codes, drug_names=[d['name'] for d in drugs_info],
|
||||
api_status='ERROR', response_time_ms=int((time_module.time() - start_time) * 1000),
|
||||
error_message=str(kims_err))
|
||||
logging.error(f"KIMS API 호출 실패: {kims_err}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
@ -4276,6 +4294,27 @@ def api_kims_interaction_check():
|
||||
for drug in drugs_info:
|
||||
drug['has_interaction'] = drug['kd_code'] in interaction_drug_codes
|
||||
|
||||
# 응답 시간 계산
|
||||
response_time_ms = int((time_module.time() - start_time) * 1000)
|
||||
|
||||
# SQLite 로깅
|
||||
try:
|
||||
log_id = log_kims_call(
|
||||
pre_serial=pre_serial,
|
||||
user_id=user_id,
|
||||
source='admin',
|
||||
drug_codes=kd_codes,
|
||||
drug_names=[d['name'] for d in drugs_info],
|
||||
api_status='SUCCESS',
|
||||
http_status=200,
|
||||
response_time_ms=response_time_ms,
|
||||
interactions=interactions,
|
||||
response_raw=kims_data
|
||||
)
|
||||
logging.info(f"KIMS 로그 저장: ID={log_id}, {len(kd_codes)}개 약품, {len(interactions)}건 상호작용")
|
||||
except Exception as log_err:
|
||||
logging.warning(f"KIMS 로깅 실패 (무시): {log_err}")
|
||||
|
||||
logging.info(f"KIMS 상호작용 체크 완료: {len(kd_codes)}개 약품, {len(interactions)}건 발견 (처방: {pre_serial})")
|
||||
|
||||
return jsonify({
|
||||
@ -4289,6 +4328,20 @@ def api_kims_interaction_check():
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
# 에러 로깅
|
||||
response_time_ms = int((time_module.time() - start_time) * 1000)
|
||||
try:
|
||||
log_kims_call(
|
||||
pre_serial=pre_serial if 'pre_serial' in dir() else None,
|
||||
source='admin',
|
||||
drug_codes=drug_codes if 'drug_codes' in dir() else [],
|
||||
api_status='ERROR',
|
||||
response_time_ms=response_time_ms,
|
||||
error_message=str(e)
|
||||
)
|
||||
except:
|
||||
pass
|
||||
|
||||
logging.error(f"KIMS 상호작용 체크 오류: {e}")
|
||||
return jsonify({
|
||||
'success': False,
|
||||
|
||||
220
backend/db/kims_logger.py
Normal file
220
backend/db/kims_logger.py
Normal file
@ -0,0 +1,220 @@
|
||||
"""
|
||||
KIMS API 로깅 모듈
|
||||
- API 호출/응답 SQLite 저장
|
||||
- AI 학습용 데이터 수집
|
||||
"""
|
||||
import sqlite3
|
||||
import json
|
||||
import os
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
|
||||
# DB 파일 경로
|
||||
DB_PATH = Path(__file__).parent / 'kims_logs.db'
|
||||
|
||||
def init_db():
|
||||
"""DB 초기화 (테이블 생성)"""
|
||||
schema_path = Path(__file__).parent / 'kims_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"KIMS 로그 DB 초기화 완료: {DB_PATH}")
|
||||
|
||||
def log_kims_call(
|
||||
pre_serial: str = None,
|
||||
user_id: int = None,
|
||||
source: str = 'admin',
|
||||
drug_codes: list = None,
|
||||
drug_names: list = None,
|
||||
api_status: str = 'SUCCESS',
|
||||
http_status: int = 200,
|
||||
response_time_ms: int = 0,
|
||||
interactions: list = None,
|
||||
response_raw: dict = None,
|
||||
error_message: str = None
|
||||
) -> int:
|
||||
"""
|
||||
KIMS API 호출 로그 저장
|
||||
|
||||
Returns:
|
||||
log_id: 생성된 로그 ID
|
||||
"""
|
||||
# DB 없으면 초기화
|
||||
if not DB_PATH.exists():
|
||||
init_db()
|
||||
|
||||
conn = sqlite3.connect(str(DB_PATH))
|
||||
cursor = conn.cursor()
|
||||
|
||||
interactions = interactions or []
|
||||
drug_codes = drug_codes or []
|
||||
drug_names = drug_names or []
|
||||
|
||||
# 심각한 상호작용 여부 (severity 1 또는 2)
|
||||
has_severe = any(
|
||||
str(i.get('severity', '5')) in ['1', '2']
|
||||
for i in interactions
|
||||
)
|
||||
|
||||
# 메인 로그 삽입
|
||||
cursor.execute("""
|
||||
INSERT INTO kims_api_logs (
|
||||
pre_serial, user_id, source,
|
||||
request_drug_codes, request_drug_names, request_drug_count,
|
||||
api_status, http_status, response_time_ms,
|
||||
interaction_count, has_severe_interaction,
|
||||
interactions_json, response_raw, error_message
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
pre_serial,
|
||||
user_id,
|
||||
source,
|
||||
json.dumps(drug_codes, ensure_ascii=False),
|
||||
json.dumps(drug_names, ensure_ascii=False),
|
||||
len(drug_codes),
|
||||
api_status,
|
||||
http_status,
|
||||
response_time_ms,
|
||||
len(interactions),
|
||||
1 if has_severe else 0,
|
||||
json.dumps(interactions, ensure_ascii=False),
|
||||
json.dumps(response_raw, ensure_ascii=False) if response_raw else None,
|
||||
error_message
|
||||
))
|
||||
|
||||
log_id = cursor.lastrowid
|
||||
|
||||
# 상호작용 상세 삽입 (정규화)
|
||||
for inter in interactions:
|
||||
cursor.execute("""
|
||||
INSERT INTO kims_interactions (
|
||||
log_id,
|
||||
drug1_code, drug1_name, drug1_generic,
|
||||
drug2_code, drug2_name, drug2_generic,
|
||||
severity_level, severity_desc,
|
||||
likelihood_level, likelihood_desc,
|
||||
observation, observation_generic,
|
||||
clinical_management, action_to_take, reference
|
||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
""", (
|
||||
log_id,
|
||||
inter.get('drug1_code'),
|
||||
inter.get('drug1_name'),
|
||||
inter.get('generic1'),
|
||||
inter.get('drug2_code'),
|
||||
inter.get('drug2_name'),
|
||||
inter.get('generic2'),
|
||||
int(inter.get('severity', 5)) if str(inter.get('severity', '')).isdigit() else None,
|
||||
inter.get('severity_text'),
|
||||
None, # likelihood_level
|
||||
inter.get('likelihood'),
|
||||
inter.get('description'),
|
||||
None, # observation_generic
|
||||
inter.get('management'),
|
||||
inter.get('action'),
|
||||
None # reference
|
||||
))
|
||||
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
return log_id
|
||||
|
||||
def get_recent_logs(limit: int = 50):
|
||||
"""최근 로그 조회"""
|
||||
if not DB_PATH.exists():
|
||||
return []
|
||||
|
||||
conn = sqlite3.connect(str(DB_PATH))
|
||||
conn.row_factory = sqlite3.Row
|
||||
cursor = conn.cursor()
|
||||
|
||||
cursor.execute("""
|
||||
SELECT * FROM kims_api_logs
|
||||
ORDER BY created_at DESC
|
||||
LIMIT ?
|
||||
""", (limit,))
|
||||
|
||||
rows = cursor.fetchall()
|
||||
conn.close()
|
||||
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
def get_log_detail(log_id: int):
|
||||
"""로그 상세 조회 (상호작용 포함)"""
|
||||
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 kims_api_logs WHERE id = ?", (log_id,))
|
||||
log = cursor.fetchone()
|
||||
|
||||
if not log:
|
||||
conn.close()
|
||||
return None
|
||||
|
||||
# 상호작용 상세
|
||||
cursor.execute("""
|
||||
SELECT * FROM kims_interactions
|
||||
WHERE log_id = ?
|
||||
ORDER BY severity_level ASC
|
||||
""", (log_id,))
|
||||
interactions = cursor.fetchall()
|
||||
|
||||
conn.close()
|
||||
|
||||
result = dict(log)
|
||||
result['interactions_detail'] = [dict(i) for i in interactions]
|
||||
|
||||
return result
|
||||
|
||||
def get_stats():
|
||||
"""통계 조회"""
|
||||
if not DB_PATH.exists():
|
||||
return {}
|
||||
|
||||
conn = sqlite3.connect(str(DB_PATH))
|
||||
conn.row_factory = sqlite3.Row
|
||||
cursor = conn.cursor()
|
||||
|
||||
# 전체 통계
|
||||
cursor.execute("""
|
||||
SELECT
|
||||
COUNT(*) as total_calls,
|
||||
SUM(CASE WHEN api_status = 'SUCCESS' THEN 1 ELSE 0 END) as success_count,
|
||||
SUM(CASE WHEN interaction_count > 0 THEN 1 ELSE 0 END) as with_interaction,
|
||||
SUM(CASE WHEN has_severe_interaction = 1 THEN 1 ELSE 0 END) as with_severe,
|
||||
AVG(response_time_ms) as avg_response_ms
|
||||
FROM kims_api_logs
|
||||
""")
|
||||
stats = dict(cursor.fetchone())
|
||||
|
||||
# 최근 7일 일별 통계
|
||||
cursor.execute("""
|
||||
SELECT * FROM kims_stats
|
||||
ORDER BY date DESC
|
||||
LIMIT 7
|
||||
""")
|
||||
daily = [dict(row) for row in cursor.fetchall()]
|
||||
|
||||
conn.close()
|
||||
|
||||
stats['daily'] = daily
|
||||
return stats
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
# DB 초기화 테스트
|
||||
init_db()
|
||||
print("KIMS 로그 DB 초기화 완료!")
|
||||
86
backend/db/kims_logs_schema.sql
Normal file
86
backend/db/kims_logs_schema.sql
Normal file
@ -0,0 +1,86 @@
|
||||
-- KIMS API 로그 테이블 스키마
|
||||
-- AI 학습 데이터로 활용 예정
|
||||
|
||||
-- 1. API 호출 로그 (메인)
|
||||
CREATE TABLE IF NOT EXISTS kims_api_logs (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 호출 컨텍스트
|
||||
pre_serial TEXT, -- 처방번호
|
||||
user_id INTEGER, -- 마일리지 회원 ID (있으면)
|
||||
source TEXT DEFAULT 'admin', -- 호출 소스 (admin, api, batch 등)
|
||||
|
||||
-- 요청 데이터
|
||||
request_drug_codes TEXT NOT NULL, -- JSON: ["055101150", "622801610"]
|
||||
request_drug_names TEXT, -- JSON: ["오메프투캡슐", "락소펜엠정"]
|
||||
request_drug_count INTEGER, -- 요청 약품 수
|
||||
|
||||
-- 응답 데이터
|
||||
api_status TEXT NOT NULL, -- SUCCESS, ERROR, TIMEOUT
|
||||
http_status INTEGER, -- HTTP 상태 코드
|
||||
response_time_ms INTEGER, -- 응답 시간 (밀리초)
|
||||
|
||||
-- 상호작용 결과
|
||||
interaction_count INTEGER DEFAULT 0, -- 발견된 상호작용 수
|
||||
has_severe_interaction INTEGER DEFAULT 0, -- 심각한 상호작용 여부 (1/2 등급)
|
||||
|
||||
-- 상세 데이터 (JSON)
|
||||
interactions_json TEXT, -- 상호작용 상세 정보 JSON
|
||||
response_raw TEXT, -- 전체 API 응답 (디버깅/학습용)
|
||||
|
||||
-- 에러 정보
|
||||
error_message TEXT
|
||||
);
|
||||
|
||||
-- 2. 상호작용 상세 (정규화, AI 학습용)
|
||||
CREATE TABLE IF NOT EXISTS kims_interactions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
log_id INTEGER NOT NULL, -- kims_api_logs.id FK
|
||||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||
|
||||
-- 약품 1
|
||||
drug1_code TEXT NOT NULL,
|
||||
drug1_name TEXT,
|
||||
drug1_generic TEXT, -- 성분명 (영문)
|
||||
|
||||
-- 약품 2
|
||||
drug2_code TEXT NOT NULL,
|
||||
drug2_name TEXT,
|
||||
drug2_generic TEXT, -- 성분명 (영문)
|
||||
|
||||
-- 상호작용 정보
|
||||
severity_level INTEGER, -- 1=심각, 2=중등도, 3=경미, 4=참고
|
||||
severity_desc TEXT, -- 심각도 설명 (중증, 경미 등)
|
||||
likelihood_level INTEGER, -- 발생 가능성
|
||||
likelihood_desc TEXT,
|
||||
|
||||
-- 상세 설명 (AI 학습 핵심 데이터)
|
||||
observation TEXT, -- 상호작용 설명 (한글)
|
||||
observation_generic TEXT, -- 일반적 설명
|
||||
clinical_management TEXT, -- 임상적 관리 방법
|
||||
action_to_take TEXT, -- 권장 조치
|
||||
reference TEXT, -- 참고문헌
|
||||
|
||||
FOREIGN KEY (log_id) REFERENCES kims_api_logs(id)
|
||||
);
|
||||
|
||||
-- 인덱스
|
||||
CREATE INDEX IF NOT EXISTS idx_kims_logs_created ON kims_api_logs(created_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_kims_logs_pre_serial ON kims_api_logs(pre_serial);
|
||||
CREATE INDEX IF NOT EXISTS idx_kims_logs_status ON kims_api_logs(api_status);
|
||||
CREATE INDEX IF NOT EXISTS idx_kims_interactions_log ON kims_interactions(log_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_kims_interactions_drugs ON kims_interactions(drug1_code, drug2_code);
|
||||
CREATE INDEX IF NOT EXISTS idx_kims_interactions_severity ON kims_interactions(severity_level);
|
||||
|
||||
-- 통계 뷰
|
||||
CREATE VIEW IF NOT EXISTS kims_stats AS
|
||||
SELECT
|
||||
DATE(created_at) as date,
|
||||
COUNT(*) as total_calls,
|
||||
SUM(CASE WHEN api_status = 'SUCCESS' THEN 1 ELSE 0 END) as success_count,
|
||||
SUM(CASE WHEN interaction_count > 0 THEN 1 ELSE 0 END) as with_interaction,
|
||||
SUM(CASE WHEN has_severe_interaction = 1 THEN 1 ELSE 0 END) as with_severe,
|
||||
AVG(response_time_ms) as avg_response_ms
|
||||
FROM kims_api_logs
|
||||
GROUP BY DATE(created_at);
|
||||
Loading…
Reference in New Issue
Block a user