diff --git a/app/app.py b/app/app.py new file mode 100644 index 0000000..913021b --- /dev/null +++ b/app/app.py @@ -0,0 +1,131 @@ +import os, sys +from flask import Flask, render_template, request, jsonify +from sqlalchemy import select, func, between, and_, or_ +from datetime import datetime, timedelta +import json + +# 경로 추가 +sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))) +from conf import db, db_schema + +app = Flask(__name__) +engine = db.engine +pos_table = db_schema.pos + +@app.route('/') +def index(): + today = datetime.today().date() + end_date = today - timedelta(days=1) + start_date = end_date - timedelta(days=6) + return render_template('index.html', start_date=start_date, end_date=end_date) + +@app.route('/api/ca01_list') +def ca01_list(): + with engine.connect() as conn: + result = conn.execute( + select(pos_table.c.ca01).distinct().order_by(pos_table.c.ca01) + ).scalars().all() + return jsonify(['전체'] + result) + +@app.route('/api/ca03_list') +def ca03_list(): + ca01 = request.args.get('ca01', None) + with engine.connect() as conn: + query = select(pos_table.c.ca03).distinct().order_by(pos_table.c.ca03) + if ca01 and ca01 != '전체': + query = query.where(pos_table.c.ca01 == ca01) + result = conn.execute(query).scalars().all() + return jsonify(['전체'] + result) + +@app.route('/search', methods=['GET']) +def search(): + start_date = request.args.get('start_date') + end_date = request.args.get('end_date') + ca01 = request.args.get('ca01') + ca03 = request.args.get('ca03') + + conditions = [between(pos_table.c.date, start_date, end_date)] + if ca01 and ca01 != '전체': + conditions.append(pos_table.c.ca01 == ca01) + if ca03 and ca03 != '전체': + conditions.append(pos_table.c.ca03 == ca03) + + with engine.connect() as conn: + stmt = select( + pos_table.c.ca01, + pos_table.c.ca02, + pos_table.c.ca03, + pos_table.c.name, + func.sum(pos_table.c.qty).label("qty"), + func.sum(pos_table.c.tot_amount).label("tot_amount"), + func.sum(pos_table.c.tot_discount).label("tot_discount"), + func.sum(pos_table.c.actual_amount).label("actual_amount") + ).where(*conditions).group_by(pos_table.c.barcode) + + result = conn.execute(stmt).mappings().all() + return jsonify([dict(row) for row in result]) + +# 월별 데이터 불러오기 +def get_monthly_visitor_data(ca01_keywords=None, ca03_includes=None): + from collections import defaultdict + from decimal import Decimal + + ca01_keywords = ca01_keywords or ['매표소'] + ca03_includes = ca03_includes or ['입장료', '티켓', '기업제휴'] + + pos = db_schema.pos + session = db.get_session() + + # 필터 조건 + ca01_conditions = [pos.c.ca01.like(f'%{kw}%') for kw in ca01_keywords] + conditions = [or_(*ca01_conditions), pos.c.ca03.in_(ca03_includes)] + + # 연도별 월별 합계 쿼리 + query = ( + session.query( + func.year(pos.c.date).label('year'), + func.month(pos.c.date).label('month'), + func.sum(pos.c.qty).label('qty') + ) + .filter(and_(*conditions)) + .group_by(func.year(pos.c.date), func.month(pos.c.date)) + .order_by(func.year(pos.c.date), func.month(pos.c.date)) + ) + + result = query.all() + session.close() + + # 결과 가공: {년도: [1~12월 값]} 형태 + data = defaultdict(lambda: [0]*12) + + for row in result: + year = int(row.year) + month = int(row.month) + qty = int(row.qty or 0) if isinstance(row.qty, Decimal) else row.qty or 0 + data[year][month - 1] = qty + + # Dict → 일반 dict 정렬 + return dict(sorted(data.items())) + +@app.route('/monthly_view.html') +def monthly_view(): + visitor_data = get_monthly_visitor_data() + visitor_data_json = json.dumps(visitor_data) # JSON 문자열로 변환 + return render_template('monthly_view.html', visitor_data=visitor_data_json) + +from lib.weekly_visitor_forecast_prophet import get_forecast_dict +from lib.weekly_visitor_forecast import get_recent_dataframe, get_last_year_dataframe + +@app.route('/2weeks_view') +def view_2weeks(): + df_recent = get_recent_dataframe() + df_prev = get_last_year_dataframe() + return render_template( + '2weeks_view.html', + recent_data=df_recent.to_dict(orient='records'), + lastyear_data=df_prev.to_dict(orient='records') + ) + +if __name__ == '__main__': + app.run(debug=True, host='0.0.0.0') + diff --git a/app/templates/2weeks.html b/app/templates/2weeks.html new file mode 100644 index 0000000..3a66550 --- /dev/null +++ b/app/templates/2weeks.html @@ -0,0 +1,61 @@ + + + + +월별 입장객 현황 + + + + +
+

직전 2주간 방문객 현황 상세 내역

+ + + + + + + + + + + + {% for d in dates %} + + {% endfor %} + + + + + + + {% for year_label, data_by_item in data.items() %} + {% set rowspan_val = data_by_item|length %} + {% for item_name, row in data_by_item.items() %} + + {% if loop.first %} + + {% endif %} + + + {% for val in row.values_list %} + + {% endfor %} + + + + {% if row.expected %} + {% for ex_val in row.expected %} + + {% endfor %} + {% else %} + + {% endif %} + + {% endfor %} + {% endfor %} + +
구분방문현황합계/평균예상
년도항목{{ d.strftime('%-m/%-d') }}1일2일3일
{{ year_label }}{{ item_name }}{{ val }}{{ row.total or '' }}{{ ex_val }}
+
+ + diff --git a/app/templates/index.html b/app/templates/index.html new file mode 100644 index 0000000..df561b4 --- /dev/null +++ b/app/templates/index.html @@ -0,0 +1,145 @@ + + + + + POS 데이터 조회 + + + +

POS 데이터 조회

+ +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ +
+
+ +
+ + + + + + + + + + + + + + + + + + +
대분류중분류소분류상품명수량총매출액총할인액실매출액
합계0000
+ + + + + diff --git a/app/templates/monthly_view.html b/app/templates/monthly_view.html new file mode 100644 index 0000000..90d761c --- /dev/null +++ b/app/templates/monthly_view.html @@ -0,0 +1,144 @@ + + + + +월별 입장객 현황 + + + + + +

월별 입장객 현황 (2017년 ~ 현재)

+ + + + + + + + + + + + + +
연도1월2월3월4월5월6월7월8월9월10월11월12월합계
+ + + + + +