feat: PMR 라벨 미리보기 기능
- /pmr/api/label/preview: PIL 렌더링 → Base64 이미지 - 미리보기 버튼 + 모달 추가 - 29mm 용지 기준 라벨 이미지 생성
This commit is contained in:
@@ -1,10 +1,14 @@
|
||||
# pmr_api.py - 조제관리(PMR) Blueprint API
|
||||
# PharmaIT3000 MSSQL 연동 (192.168.0.4)
|
||||
|
||||
from flask import Blueprint, jsonify, request, render_template
|
||||
from flask import Blueprint, jsonify, request, render_template, send_file
|
||||
import pyodbc
|
||||
from datetime import datetime, date
|
||||
import logging
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
import io
|
||||
import base64
|
||||
import os
|
||||
|
||||
pmr_bp = Blueprint('pmr', __name__, url_prefix='/pmr')
|
||||
|
||||
@@ -319,3 +323,191 @@ def test_connection():
|
||||
})
|
||||
except Exception as e:
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
# API: 라벨 미리보기
|
||||
# ─────────────────────────────────────────────────────────────
|
||||
@pmr_bp.route('/api/label/preview', methods=['POST'])
|
||||
def preview_label():
|
||||
"""
|
||||
라벨 미리보기 (PIL 렌더링 → Base64 이미지)
|
||||
|
||||
Request Body:
|
||||
- patient_name: 환자명
|
||||
- med_name: 약품명
|
||||
- dosage: 1회 복용량
|
||||
- frequency: 복용 횟수
|
||||
- duration: 복용 일수
|
||||
- unit: 단위 (정, 캡슐, mL 등)
|
||||
"""
|
||||
try:
|
||||
data = request.get_json()
|
||||
|
||||
patient_name = data.get('patient_name', '')
|
||||
med_name = data.get('med_name', '')
|
||||
dosage = float(data.get('dosage', 0))
|
||||
frequency = int(data.get('frequency', 0))
|
||||
duration = int(data.get('duration', 0))
|
||||
unit = data.get('unit', '정')
|
||||
|
||||
# 라벨 이미지 생성
|
||||
image = create_label_image(
|
||||
patient_name=patient_name,
|
||||
med_name=med_name,
|
||||
dosage=dosage,
|
||||
frequency=frequency,
|
||||
duration=duration,
|
||||
unit=unit
|
||||
)
|
||||
|
||||
# Base64 인코딩
|
||||
buffer = io.BytesIO()
|
||||
image.save(buffer, format='PNG')
|
||||
buffer.seek(0)
|
||||
img_base64 = base64.b64encode(buffer.getvalue()).decode('utf-8')
|
||||
|
||||
return jsonify({
|
||||
'success': True,
|
||||
'image': f'data:image/png;base64,{img_base64}'
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
logging.error(f"라벨 미리보기 오류: {e}")
|
||||
return jsonify({'success': False, 'error': str(e)}), 500
|
||||
|
||||
|
||||
def create_label_image(patient_name, med_name, dosage, frequency, duration, unit='정'):
|
||||
"""
|
||||
라벨 이미지 생성 (29mm 용지 기준)
|
||||
"""
|
||||
# 라벨 크기 (29mm 용지, 300dpi 기준)
|
||||
label_width = 306
|
||||
label_height = 380
|
||||
|
||||
image = Image.new("RGB", (label_width, label_height), "white")
|
||||
draw = ImageDraw.Draw(image)
|
||||
|
||||
# 폰트 설정 (Windows 경로)
|
||||
font_path = "C:/Windows/Fonts/malgunbd.ttf"
|
||||
if not os.path.exists(font_path):
|
||||
font_path = "C:/Windows/Fonts/malgun.ttf"
|
||||
|
||||
try:
|
||||
name_font = ImageFont.truetype(font_path, 36)
|
||||
drug_font = ImageFont.truetype(font_path, 24)
|
||||
info_font = ImageFont.truetype(font_path, 22)
|
||||
small_font = ImageFont.truetype(font_path, 18)
|
||||
except:
|
||||
name_font = ImageFont.load_default()
|
||||
drug_font = ImageFont.load_default()
|
||||
info_font = ImageFont.load_default()
|
||||
small_font = ImageFont.load_default()
|
||||
|
||||
# 중앙 정렬 텍스트 함수
|
||||
def draw_centered(text, y, font, fill="black"):
|
||||
bbox = draw.textbbox((0, 0), text, font=font)
|
||||
w = bbox[2] - bbox[0]
|
||||
x = (label_width - w) // 2
|
||||
draw.text((x, y), text, font=font, fill=fill)
|
||||
return y + bbox[3] - bbox[1] + 5
|
||||
|
||||
# 약품명 줄바꿈 처리
|
||||
def wrap_text(text, font, max_width):
|
||||
lines = []
|
||||
words = text.split()
|
||||
current_line = ""
|
||||
for word in words:
|
||||
test_line = f"{current_line} {word}".strip()
|
||||
bbox = draw.textbbox((0, 0), test_line, font=font)
|
||||
if bbox[2] - bbox[0] <= max_width:
|
||||
current_line = test_line
|
||||
else:
|
||||
if current_line:
|
||||
lines.append(current_line)
|
||||
current_line = word
|
||||
if current_line:
|
||||
lines.append(current_line)
|
||||
return lines if lines else [text]
|
||||
|
||||
y = 15
|
||||
|
||||
# 환자명 (띄어쓰기)
|
||||
spaced_name = " ".join(patient_name) if patient_name else ""
|
||||
y = draw_centered(spaced_name, y, name_font)
|
||||
|
||||
y += 5
|
||||
|
||||
# 약품명 (줄바꿈)
|
||||
# 괄호 앞에서 분리
|
||||
if '(' in med_name:
|
||||
main_name = med_name.split('(')[0].strip()
|
||||
sub_info = '(' + med_name.split('(', 1)[1] if '(' in med_name else ''
|
||||
else:
|
||||
main_name = med_name
|
||||
sub_info = ''
|
||||
|
||||
# 약품명 줄바꿈
|
||||
name_lines = wrap_text(main_name, drug_font, label_width - 30)
|
||||
for line in name_lines:
|
||||
y = draw_centered(line, y, drug_font)
|
||||
|
||||
# 부가정보
|
||||
if sub_info:
|
||||
y = draw_centered(sub_info, y, small_font, fill="gray")
|
||||
|
||||
y += 5
|
||||
|
||||
# 총량 계산
|
||||
if dosage > 0 and frequency > 0 and duration > 0:
|
||||
total = dosage * frequency * duration
|
||||
total_str = str(int(total)) if total == int(total) else f"{total:.1f}"
|
||||
total_text = f"총 {total_str}{unit} / {duration}일분"
|
||||
y = draw_centered(total_text, y, info_font)
|
||||
|
||||
y += 5
|
||||
|
||||
# 용법 박스
|
||||
box_margin = 20
|
||||
box_top = y
|
||||
box_bottom = y + 70
|
||||
draw.rectangle(
|
||||
[(box_margin, box_top), (label_width - box_margin, box_bottom)],
|
||||
outline="black",
|
||||
width=2
|
||||
)
|
||||
|
||||
# 박스 내용
|
||||
dosage_str = str(int(dosage)) if dosage == int(dosage) else f"{dosage:.2f}".rstrip('0').rstrip('.')
|
||||
dosage_text = f"{dosage_str}{unit}"
|
||||
|
||||
# 복용 시간
|
||||
if frequency == 1:
|
||||
time_text = "아침"
|
||||
elif frequency == 2:
|
||||
time_text = "아침, 저녁"
|
||||
elif frequency == 3:
|
||||
time_text = "아침, 점심, 저녁"
|
||||
else:
|
||||
time_text = f"1일 {frequency}회"
|
||||
|
||||
box_center_y = (box_top + box_bottom) // 2
|
||||
draw_centered(dosage_text, box_center_y - 20, info_font)
|
||||
draw_centered(time_text, box_center_y + 5, info_font)
|
||||
|
||||
y = box_bottom + 10
|
||||
|
||||
# 조제일
|
||||
today = datetime.now().strftime('%Y-%m-%d')
|
||||
y = draw_centered(f"조제일: {today}", y, small_font)
|
||||
|
||||
# 약국명 (하단)
|
||||
pharmacy_y = label_height - 40
|
||||
draw.rectangle(
|
||||
[(50, pharmacy_y - 5), (label_width - 50, pharmacy_y + 25)],
|
||||
outline="black",
|
||||
width=1
|
||||
)
|
||||
draw_centered("청 춘 약 국", pharmacy_y, info_font)
|
||||
|
||||
return image
|
||||
|
||||
Reference in New Issue
Block a user