웹 stats UI 에서 '보훈100%', '보훈60%' 대신 raw 코드 '4_1', '4_2' 가 그대로 노출되던 문제 수정. v1_pharmit3000.py 가 QT-POS 와 동기화되면서 보훈 세분화 키 (4_1~4_4) 를 반환하기 시작했으나, app.py 의 GUBUN_LABEL dict 에 해당 키가 없어 dict.get(code, code) fallback 으로 날 코드가 그대로 노출됐음. pharmon-web/sales_stats_dialog.py 의 _GUBUN_LABEL 과 완전 동기화: - 4_1: 보훈100% (GITA_GUBUN=1) - 4_2: 보훈60% (GITA_GUBUN=2) - 4_3: 보훈50% (GITA_GUBUN=3) - 4_4: 보훈30% (GITA_GUBUN=4) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
322 lines
8.8 KiB
Python
322 lines
8.8 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
Pharmacy Stats API - Flask 서버
|
|
|
|
QT-POS 통계 다이얼로그 API 버전
|
|
v1: PharmIT3000
|
|
v2: PMPLUS20
|
|
"""
|
|
from flask import Flask, jsonify, request, render_template
|
|
from datetime import date, timedelta
|
|
|
|
from queries import v1_pharmit3000 as v1
|
|
from queries import v2_pmplus20 as v2
|
|
|
|
app = Flask(__name__)
|
|
|
|
# 보험구분 라벨 (pharmon-web/sales_stats_dialog.py _GUBUN_LABEL 와 동기화)
|
|
GUBUN_LABEL = {
|
|
'0': '건강보험',
|
|
'1': '의료급여',
|
|
'2': '산재',
|
|
'3': '자동차',
|
|
'4': '보훈', # 기타 보훈 (GITA_GUBUN 없을 때)
|
|
'4_1': '보훈100%', # GITA_GUBUN=1
|
|
'4_2': '보훈60%', # GITA_GUBUN=2
|
|
'4_3': '보훈50%', # GITA_GUBUN=3
|
|
'4_4': '보훈30%', # GITA_GUBUN=4
|
|
'5': '공상',
|
|
'6': '본인',
|
|
'7': '차상위1',
|
|
'8': '희귀',
|
|
'9': '비급여',
|
|
'E': '차상위2',
|
|
'F': '차상위2',
|
|
}
|
|
|
|
|
|
@app.route('/')
|
|
def index():
|
|
return render_template('stats.html')
|
|
|
|
|
|
# ============== v1 API (PharmIT3000) ==============
|
|
|
|
@app.route('/v1/api/stats')
|
|
def v1_stats():
|
|
"""v1 전체 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
try:
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
result['version'] = 'v1'
|
|
result['source'] = 'PharmIT3000'
|
|
result['date_from'] = date_from
|
|
result['date_to'] = date_to
|
|
return jsonify(result)
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@app.route('/v1/api/stats/insurance')
|
|
def v1_stats_insurance():
|
|
"""v1 보험별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
|
|
by_gubun = []
|
|
for code, data in result.get('by_gubun', {}).items():
|
|
by_gubun.append({
|
|
'code': code,
|
|
'label': GUBUN_LABEL.get(code, code),
|
|
**data
|
|
})
|
|
|
|
return jsonify({
|
|
'version': 'v1',
|
|
'source': 'PharmIT3000',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_gubun': sorted(by_gubun, key=lambda x: x['code'])
|
|
})
|
|
|
|
|
|
@app.route('/v1/api/stats/time')
|
|
def v1_stats_time():
|
|
"""v1 시간가산별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
|
|
return jsonify({
|
|
'version': 'v1',
|
|
'source': 'PharmIT3000',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_time': result['by_time']
|
|
})
|
|
|
|
|
|
@app.route('/v1/api/stats/payment')
|
|
def v1_stats_payment():
|
|
"""v1 결제수단별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
|
|
return jsonify({
|
|
'version': 'v1',
|
|
'source': 'PharmIT3000',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_pay': result['by_pay']
|
|
})
|
|
|
|
|
|
@app.route('/v1/api/stats/hospital')
|
|
def v1_stats_hospital():
|
|
"""v1 병원별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v1.get_sales_stats(date_from, date_to)
|
|
|
|
by_hosp = []
|
|
for name, data in result.get('by_hosp', {}).items():
|
|
by_hosp.append({'name': name, **data})
|
|
|
|
return jsonify({
|
|
'version': 'v1',
|
|
'source': 'PharmIT3000',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_hosp': sorted(by_hosp, key=lambda x: -x['cnt'])[:50] # 상위 50개
|
|
})
|
|
|
|
|
|
# ============== v2 API (PMPLUS20) ==============
|
|
|
|
@app.route('/v2/api/stats')
|
|
def v2_stats():
|
|
"""v2 전체 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
try:
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
result['version'] = 'v2'
|
|
result['source'] = 'PMPLUS20'
|
|
result['date_from'] = date_from
|
|
result['date_to'] = date_to
|
|
return jsonify(result)
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@app.route('/v2/api/stats/insurance')
|
|
def v2_stats_insurance():
|
|
"""v2 보험별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
|
|
if 'error' in result:
|
|
return jsonify(result), 500
|
|
|
|
by_gubun = []
|
|
for code, data in result.get('by_gubun', {}).items():
|
|
by_gubun.append({
|
|
'code': code,
|
|
'label': GUBUN_LABEL.get(code, code),
|
|
**data
|
|
})
|
|
|
|
return jsonify({
|
|
'version': 'v2',
|
|
'source': 'PMPLUS20',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_gubun': sorted(by_gubun, key=lambda x: x['code'])
|
|
})
|
|
|
|
|
|
@app.route('/v2/api/stats/time')
|
|
def v2_stats_time():
|
|
"""v2 시간가산별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
if 'error' in result:
|
|
return jsonify(result), 500
|
|
|
|
return jsonify({
|
|
'version': 'v2',
|
|
'source': 'PMPLUS20',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_time': result['by_time']
|
|
})
|
|
|
|
|
|
@app.route('/v2/api/stats/payment')
|
|
def v2_stats_payment():
|
|
"""v2 결제수단별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
if 'error' in result:
|
|
return jsonify(result), 500
|
|
|
|
return jsonify({
|
|
'version': 'v2',
|
|
'source': 'PMPLUS20',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_pay': result['by_pay']
|
|
})
|
|
|
|
|
|
@app.route('/v2/api/stats/hospital')
|
|
def v2_stats_hospital():
|
|
"""v2 병원별 통계"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
result = v2.get_sales_stats(date_from, date_to)
|
|
if 'error' in result:
|
|
return jsonify(result), 500
|
|
|
|
by_hosp = []
|
|
for name, data in result.get('by_hosp', {}).items():
|
|
by_hosp.append({'name': name, **data})
|
|
|
|
return jsonify({
|
|
'version': 'v2',
|
|
'source': 'PMPLUS20',
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'total': result['total'],
|
|
'by_hosp': sorted(by_hosp, key=lambda x: -x['cnt'])[:50]
|
|
})
|
|
|
|
|
|
# ============== 비교 API ==============
|
|
|
|
@app.route('/api/compare')
|
|
def compare():
|
|
"""v1 vs v2 비교"""
|
|
date_from = request.args.get('from', _default_from())
|
|
date_to = request.args.get('to', _default_to())
|
|
|
|
v1_result = v1.get_sales_stats(date_from, date_to)
|
|
v2_result = v2.get_sales_stats(date_from, date_to)
|
|
|
|
# 차이 계산
|
|
diff = {}
|
|
if 'total' in v1_result and 'total' in v2_result:
|
|
v1_total = v1_result['total']
|
|
v2_total = v2_result['total']
|
|
for key in v1_total:
|
|
v1_val = v1_total.get(key, 0)
|
|
v2_val = v2_total.get(key, 0)
|
|
diff[key] = v2_val - v1_val
|
|
|
|
return jsonify({
|
|
'date_from': date_from,
|
|
'date_to': date_to,
|
|
'v1': {
|
|
'source': 'PharmIT3000',
|
|
'total': v1_result.get('total', {}),
|
|
'error': v1_result.get('error')
|
|
},
|
|
'v2': {
|
|
'source': 'PMPLUS20',
|
|
'total': v2_result.get('total', {}),
|
|
'error': v2_result.get('error')
|
|
},
|
|
'diff': diff
|
|
})
|
|
|
|
|
|
def _default_from():
|
|
"""기본 시작일: 이번 달 1일"""
|
|
today = date.today()
|
|
return today.replace(day=1).strftime('%Y%m%d')
|
|
|
|
|
|
def _default_to():
|
|
"""기본 종료일: 오늘"""
|
|
return date.today().strftime('%Y%m%d')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
print("🏥 Pharmacy Stats API")
|
|
print("http://0.0.0.0:5060")
|
|
print("")
|
|
print("Endpoints:")
|
|
print(" /v1/api/stats - PharmIT3000 전체 통계")
|
|
print(" /v1/api/stats/insurance - PharmIT3000 보험별")
|
|
print(" /v1/api/stats/time - PharmIT3000 시간가산별")
|
|
print(" /v1/api/stats/payment - PharmIT3000 결제수단별")
|
|
print(" /v1/api/stats/hospital - PharmIT3000 병원별")
|
|
print("")
|
|
print(" /v2/api/stats/... - PMPLUS20 (동일 구조)")
|
|
print("")
|
|
print(" /api/compare - v1 vs v2 비교")
|
|
app.run(host='0.0.0.0', port=5050, debug=True)
|