- Flask Blueprint 아키텍처로 전환 (dashboard, upload, backup, status) - app.py 681줄 95줄로 축소 (86% 감소) - HTML 템플릿 모듈화 (base.html + 기능별 templates) - CSS/JS 파일 분리 (common + 기능별 파일) - 대시보드 기능 추가 (통계, 주간 예보, 방문객 추이) - 파일 업로드 웹 인터페이스 구현 - 백업/복구 관리 UI 구현 - Docker 배포 환경 개선 - .gitignore 업데이트 (uploads, backups, cache 등)
226 lines
7.1 KiB
Python
226 lines
7.1 KiB
Python
# app/blueprints/dashboard.py
|
|
"""
|
|
대시보드 블루프린트
|
|
|
|
역할:
|
|
- 데이터 통계 조회 (OKPOS, UPSolution, 날씨)
|
|
- 주간 예보 조회
|
|
- 방문객 추이 차트 데이터 제공
|
|
"""
|
|
|
|
from flask import Blueprint, render_template, jsonify, request
|
|
from datetime import datetime, timedelta
|
|
from sqlalchemy import select, func, desc
|
|
|
|
import sys
|
|
import os
|
|
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..')))
|
|
|
|
from conf import db, db_schema
|
|
|
|
dashboard_bp = Blueprint('dashboard', __name__, url_prefix='/api/dashboard')
|
|
|
|
|
|
@dashboard_bp.route('/')
|
|
def index():
|
|
"""대시보드 페이지"""
|
|
return render_template('dashboard.html')
|
|
|
|
|
|
@dashboard_bp.route('/okpos-product', methods=['GET'])
|
|
def get_okpos_product_stats():
|
|
"""OKPOS 상품별 통계"""
|
|
try:
|
|
session = db.get_session()
|
|
|
|
# 데이터 개수
|
|
total_records = session.query(db_schema.OkposProduct).count()
|
|
|
|
# 데이터 보유 일수
|
|
earliest = session.query(func.min(db_schema.OkposProduct.data_date)).scalar()
|
|
latest = session.query(func.max(db_schema.OkposProduct.data_date)).scalar()
|
|
|
|
if earliest and latest:
|
|
total_days = (latest - earliest).days + 1
|
|
last_date = latest.strftime('%Y-%m-%d')
|
|
else:
|
|
total_days = 0
|
|
last_date = None
|
|
|
|
session.close()
|
|
|
|
return jsonify({
|
|
'total_records': total_records,
|
|
'total_days': total_days,
|
|
'last_date': last_date
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@dashboard_bp.route('/okpos-receipt', methods=['GET'])
|
|
def get_okpos_receipt_stats():
|
|
"""OKPOS 영수증 통계"""
|
|
try:
|
|
session = db.get_session()
|
|
|
|
total_records = session.query(db_schema.OkposReceipt).count()
|
|
|
|
earliest = session.query(func.min(db_schema.OkposReceipt.receipt_date)).scalar()
|
|
latest = session.query(func.max(db_schema.OkposReceipt.receipt_date)).scalar()
|
|
|
|
if earliest and latest:
|
|
total_days = (latest - earliest).days + 1
|
|
last_date = latest.strftime('%Y-%m-%d')
|
|
else:
|
|
total_days = 0
|
|
last_date = None
|
|
|
|
session.close()
|
|
|
|
return jsonify({
|
|
'total_records': total_records,
|
|
'total_days': total_days,
|
|
'last_date': last_date
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@dashboard_bp.route('/upsolution', methods=['GET'])
|
|
def get_upsolution_stats():
|
|
"""UPSolution 통계"""
|
|
try:
|
|
session = db.get_session()
|
|
|
|
total_records = session.query(db_schema.Upsolution).count()
|
|
|
|
earliest = session.query(func.min(db_schema.Upsolution.sales_date)).scalar()
|
|
latest = session.query(func.max(db_schema.Upsolution.sales_date)).scalar()
|
|
|
|
if earliest and latest:
|
|
total_days = (latest - earliest).days + 1
|
|
last_date = latest.strftime('%Y-%m-%d')
|
|
else:
|
|
total_days = 0
|
|
last_date = None
|
|
|
|
session.close()
|
|
|
|
return jsonify({
|
|
'total_records': total_records,
|
|
'total_days': total_days,
|
|
'last_date': last_date
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@dashboard_bp.route('/weather', methods=['GET'])
|
|
def get_weather_stats():
|
|
"""날씨 데이터 통계"""
|
|
try:
|
|
session = db.get_session()
|
|
|
|
total_records = session.query(db_schema.Weather).count()
|
|
|
|
earliest = session.query(func.min(db_schema.Weather.date)).scalar()
|
|
latest = session.query(func.max(db_schema.Weather.date)).scalar()
|
|
|
|
if earliest and latest:
|
|
total_days = (latest - earliest).days + 1
|
|
last_date = latest.strftime('%Y-%m-%d')
|
|
else:
|
|
total_days = 0
|
|
last_date = None
|
|
|
|
session.close()
|
|
|
|
return jsonify({
|
|
'total_records': total_records,
|
|
'total_days': total_days,
|
|
'last_date': last_date
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@dashboard_bp.route('/weekly-forecast', methods=['GET'])
|
|
def get_weekly_forecast():
|
|
"""이번주 예상 날씨 & 방문객"""
|
|
try:
|
|
session = db.get_session()
|
|
|
|
# 오늘부터 7일간 데이터 조회
|
|
today = datetime.now().date()
|
|
end_date = today + timedelta(days=6)
|
|
|
|
forecast_data = []
|
|
|
|
for i in range(7):
|
|
current_date = today + timedelta(days=i)
|
|
day_name = ['월', '화', '수', '목', '금', '토', '일'][current_date.weekday()]
|
|
|
|
# 날씨 데이터
|
|
weather = session.query(db_schema.Weather).filter(
|
|
db_schema.Weather.date == current_date
|
|
).first()
|
|
|
|
# 예상 방문객 (동일한 요일의 평균)
|
|
same_day_visitors = session.query(
|
|
func.avg(db_schema.DailyVisitor.visitors)
|
|
).filter(
|
|
func.dayofweek(db_schema.DailyVisitor.visit_date) == (current_date.weekday() + 2) % 7 + 1
|
|
).scalar()
|
|
|
|
forecast_data.append({
|
|
'date': current_date.strftime('%Y-%m-%d'),
|
|
'day': day_name,
|
|
'min_temp': weather.min_temp if weather else None,
|
|
'max_temp': weather.max_temp if weather else None,
|
|
'precipitation': weather.precipitation if weather else 0.0,
|
|
'humidity': weather.humidity if weather else None,
|
|
'expected_visitors': int(same_day_visitors) if same_day_visitors else 0
|
|
})
|
|
|
|
session.close()
|
|
|
|
return jsonify({'forecast_data': forecast_data})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|
|
|
|
|
|
@dashboard_bp.route('/visitor-trend', methods=['GET'])
|
|
def get_visitor_trend():
|
|
"""방문객 추이 데이터"""
|
|
try:
|
|
start_date_str = request.args.get('start_date')
|
|
end_date_str = request.args.get('end_date')
|
|
|
|
if not start_date_str or not end_date_str:
|
|
return jsonify({'error': '날짜 범위를 지정하세요.'}), 400
|
|
|
|
start_date = datetime.strptime(start_date_str, '%Y-%m-%d').date()
|
|
end_date = datetime.strptime(end_date_str, '%Y-%m-%d').date()
|
|
|
|
session = db.get_session()
|
|
|
|
visitors = session.query(
|
|
db_schema.DailyVisitor.visit_date,
|
|
db_schema.DailyVisitor.visitors
|
|
).filter(
|
|
db_schema.DailyVisitor.visit_date.between(start_date, end_date)
|
|
).order_by(db_schema.DailyVisitor.visit_date).all()
|
|
|
|
session.close()
|
|
|
|
dates = [v[0].strftime('%Y-%m-%d') for v in visitors]
|
|
visitor_counts = [v[1] for v in visitors]
|
|
|
|
return jsonify({
|
|
'dates': dates,
|
|
'visitors': visitor_counts
|
|
})
|
|
except Exception as e:
|
|
return jsonify({'error': str(e)}), 500
|