- 프론트엔드: 조제 시 실시간 커스텀 처방 감지 - 처방 선택 시 원래 구성 약재 저장 - 약재 추가/삭제/변경 시 즉시 감지 - 가감방 뱃지 및 변경 내용 표시 - 백엔드: 커스텀 처방 자동 감지 및 저장 - compounds 테이블에 커스텀 관련 필드 추가 - 조제 시 원 처방과 비교하여 변경사항 자동 감지 - 커스텀 처방 정보 저장 (추가/제거/변경된 약재) - 환자 조제 내역에 커스텀 처방 표시 - 가감방 뱃지 표시 - 변경 내용 상세 표시 - DB 마이그레이션 스크립트 추가 - is_custom, custom_summary, custom_type 필드 추가 - compound_ingredients에 modification_type, original_grams 필드 추가 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
224 lines
7.0 KiB
Python
224 lines
7.0 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
"""
|
|
커스텀 처방 감지 유틸리티
|
|
조제 시 원 처방과 다른 구성인지 확인
|
|
"""
|
|
|
|
import sqlite3
|
|
from typing import Dict, List, Tuple
|
|
|
|
def get_connection():
|
|
"""데이터베이스 연결"""
|
|
return sqlite3.connect('database/kdrug.db')
|
|
|
|
def check_custom_prescription(compound_id: int) -> Tuple[bool, Dict]:
|
|
"""
|
|
조제가 원 처방과 다른지 확인
|
|
|
|
Returns:
|
|
(is_custom, differences_dict)
|
|
"""
|
|
conn = get_connection()
|
|
cursor = conn.cursor()
|
|
|
|
# 1. compound의 formula_id 가져오기
|
|
cursor.execute("""
|
|
SELECT c.formula_id, f.formula_name
|
|
FROM compounds c
|
|
JOIN formulas f ON c.formula_id = f.formula_id
|
|
WHERE c.compound_id = ?
|
|
""", (compound_id,))
|
|
|
|
result = cursor.fetchone()
|
|
if not result:
|
|
conn.close()
|
|
return False, {"error": "Compound not found"}
|
|
|
|
formula_id, formula_name = result
|
|
|
|
# 2. 원 처방의 구성 약재
|
|
cursor.execute("""
|
|
SELECT
|
|
fi.herb_item_id,
|
|
h.herb_name,
|
|
fi.grams_per_cheop
|
|
FROM formula_ingredients fi
|
|
JOIN herb_items h ON fi.herb_item_id = h.herb_item_id
|
|
WHERE fi.formula_id = ?
|
|
ORDER BY fi.herb_item_id
|
|
""", (formula_id,))
|
|
|
|
original_ingredients = {row[0]: {
|
|
'herb_name': row[1],
|
|
'grams_per_cheop': row[2]
|
|
} for row in cursor.fetchall()}
|
|
|
|
# 3. 실제 조제된 구성 약재
|
|
cursor.execute("""
|
|
SELECT
|
|
ci.herb_item_id,
|
|
h.herb_name,
|
|
ci.grams_per_cheop
|
|
FROM compound_ingredients ci
|
|
JOIN herb_items h ON ci.herb_item_id = h.herb_item_id
|
|
WHERE ci.compound_id = ?
|
|
ORDER BY ci.herb_item_id
|
|
""", (compound_id,))
|
|
|
|
actual_ingredients = {row[0]: {
|
|
'herb_name': row[1],
|
|
'grams_per_cheop': row[2]
|
|
} for row in cursor.fetchall()}
|
|
|
|
conn.close()
|
|
|
|
# 4. 비교 분석
|
|
differences = {
|
|
'formula_name': formula_name,
|
|
'added': [],
|
|
'removed': [],
|
|
'modified': [],
|
|
'is_custom': False
|
|
}
|
|
|
|
# 추가된 약재
|
|
for herb_id, info in actual_ingredients.items():
|
|
if herb_id not in original_ingredients:
|
|
differences['added'].append({
|
|
'herb_id': herb_id,
|
|
'herb_name': info['herb_name'],
|
|
'grams_per_cheop': info['grams_per_cheop']
|
|
})
|
|
differences['is_custom'] = True
|
|
|
|
# 제거된 약재
|
|
for herb_id, info in original_ingredients.items():
|
|
if herb_id not in actual_ingredients:
|
|
differences['removed'].append({
|
|
'herb_id': herb_id,
|
|
'herb_name': info['herb_name'],
|
|
'grams_per_cheop': info['grams_per_cheop']
|
|
})
|
|
differences['is_custom'] = True
|
|
|
|
# 용량 변경된 약재
|
|
for herb_id in set(original_ingredients.keys()) & set(actual_ingredients.keys()):
|
|
orig_grams = original_ingredients[herb_id]['grams_per_cheop']
|
|
actual_grams = actual_ingredients[herb_id]['grams_per_cheop']
|
|
|
|
if abs(orig_grams - actual_grams) > 0.01: # 부동소수점 오차 고려
|
|
differences['modified'].append({
|
|
'herb_id': herb_id,
|
|
'herb_name': original_ingredients[herb_id]['herb_name'],
|
|
'original_grams': orig_grams,
|
|
'actual_grams': actual_grams,
|
|
'difference': actual_grams - orig_grams
|
|
})
|
|
differences['is_custom'] = True
|
|
|
|
return differences['is_custom'], differences
|
|
|
|
def generate_custom_summary(differences: Dict) -> str:
|
|
"""커스텀 내역을 요약 문자열로 생성"""
|
|
summary_parts = []
|
|
|
|
# 추가
|
|
if differences['added']:
|
|
added_herbs = [f"{item['herb_name']} {item['grams_per_cheop']}g"
|
|
for item in differences['added']]
|
|
summary_parts.append(f"추가: {', '.join(added_herbs)}")
|
|
|
|
# 제거
|
|
if differences['removed']:
|
|
removed_herbs = [item['herb_name'] for item in differences['removed']]
|
|
summary_parts.append(f"제거: {', '.join(removed_herbs)}")
|
|
|
|
# 수정
|
|
if differences['modified']:
|
|
modified_herbs = [f"{item['herb_name']} {item['original_grams']}g→{item['actual_grams']}g"
|
|
for item in differences['modified']]
|
|
summary_parts.append(f"변경: {', '.join(modified_herbs)}")
|
|
|
|
return " | ".join(summary_parts) if summary_parts else "표준 처방"
|
|
|
|
def list_all_custom_prescriptions():
|
|
"""모든 커스텀 처방 찾기"""
|
|
conn = get_connection()
|
|
cursor = conn.cursor()
|
|
|
|
# 모든 조제 목록
|
|
cursor.execute("""
|
|
SELECT
|
|
c.compound_id,
|
|
c.compound_date,
|
|
p.name as patient_name,
|
|
f.formula_name
|
|
FROM compounds c
|
|
LEFT JOIN patients p ON c.patient_id = p.patient_id
|
|
JOIN formulas f ON c.formula_id = f.formula_id
|
|
ORDER BY c.compound_date DESC
|
|
""")
|
|
|
|
compounds = cursor.fetchall()
|
|
conn.close()
|
|
|
|
custom_compounds = []
|
|
|
|
for compound in compounds:
|
|
compound_id = compound[0]
|
|
is_custom, differences = check_custom_prescription(compound_id)
|
|
|
|
if is_custom:
|
|
custom_compounds.append({
|
|
'compound_id': compound_id,
|
|
'compound_date': compound[1],
|
|
'patient_name': compound[2],
|
|
'formula_name': compound[3],
|
|
'summary': generate_custom_summary(differences),
|
|
'differences': differences
|
|
})
|
|
|
|
return custom_compounds
|
|
|
|
def demo():
|
|
"""데모 실행"""
|
|
print("\n" + "="*80)
|
|
print("커스텀 처방 감지 시스템")
|
|
print("="*80)
|
|
|
|
# 전체 커스텀 처방 검색
|
|
custom_prescriptions = list_all_custom_prescriptions()
|
|
|
|
if not custom_prescriptions:
|
|
print("\n조제 내역이 없거나 모든 조제가 표준 처방입니다.")
|
|
|
|
# 테스트용 샘플 데이터 표시
|
|
print("\n[시뮬레이션] 만약 십전대보탕에 구기자를 추가했다면:")
|
|
print("-" * 60)
|
|
|
|
sample_diff = {
|
|
'formula_name': '십전대보탕',
|
|
'added': [{'herb_name': '구기자', 'grams_per_cheop': 3}],
|
|
'removed': [],
|
|
'modified': [{'herb_name': '인삼', 'original_grams': 5, 'actual_grams': 7}],
|
|
'is_custom': True
|
|
}
|
|
|
|
summary = generate_custom_summary(sample_diff)
|
|
print(f"처방: 십전대보탕 (가감방)")
|
|
print(f"변경 내역: {summary}")
|
|
print("\n환자 기록 표시:")
|
|
print(" 2024-02-17 십전대보탕 가감방 20첩")
|
|
print(f" └─ {summary}")
|
|
else:
|
|
print(f"\n총 {len(custom_prescriptions)}개의 커스텀 처방이 발견되었습니다.\n")
|
|
|
|
for cp in custom_prescriptions:
|
|
print(f"조제 #{cp['compound_id']} | {cp['compound_date']} | {cp['patient_name']}")
|
|
print(f" 처방: {cp['formula_name']} (가감방)")
|
|
print(f" 변경: {cp['summary']}")
|
|
print()
|
|
|
|
if __name__ == "__main__":
|
|
demo() |