Files
static/app/blueprints/dashboard.py
KWON 7121f250bc feat: Flask 애플리케이션 모듈화 및 웹 대시보드 구현
- Flask Blueprint 아키텍처로 전환 (dashboard, upload, backup, status)
- app.py 681줄  95줄로 축소 (86% 감소)
- HTML 템플릿 모듈화 (base.html + 기능별 templates)
- CSS/JS 파일 분리 (common + 기능별 파일)
- 대시보드 기능 추가 (통계, 주간 예보, 방문객 추이)
- 파일 업로드 웹 인터페이스 구현
- 백업/복구 관리 UI 구현
- Docker 배포 환경 개선
- .gitignore 업데이트 (uploads, backups, cache 등)
2025-12-26 17:31:37 +09:00

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