Files
static/lib/weekly_visitor_forecast_to_excel.py

151 lines
5.3 KiB
Python

import pandas as pd
from openpyxl import Workbook
from openpyxl.styles import Font, Alignment, Border, Side
from openpyxl.chart import LineChart, Reference
from openpyxl.chart.series import SeriesLabel
from datetime import date
import os
def generate_excel_report(today, recent_dates, prev_year_dates, recent_data, prev_year_data, filename="visitor_report.xlsx"):
weekday_names = ['', '', '', '', '', '', '']
wb = Workbook()
ws = wb.active
ws.title = "방문자 리포트"
bold = Font(bold=True)
center = Alignment(horizontal='center', vertical='center')
thick_border = Border(
left=Side(style='thick'), right=Side(style='thick'),
top=Side(style='thick'), bottom=Side(style='thick')
)
def fmt(d):
return f"{d.month}{d.day}{weekday_names[d.weekday()]}"
headers = ["구분"] + [fmt(d) for d in recent_dates]
ws.append([])
for _ in range(23):
ws.append([])
data_start_row = 24
ws.append(headers)
# 범례 영역
ws.merge_cells(start_row=data_start_row, start_column=1, end_row=data_start_row + 6, end_column=1)
ws.merge_cells(start_row=data_start_row + 7, start_column=1, end_row=data_start_row + 13, end_column=1)
ws.cell(row=data_start_row, column=1, value=f"{today.year}").font = bold
ws.cell(row=data_start_row + 7, column=1, value=f"{today.year - 1}").font = bold
def row(label, key, data, suffix="", fmt_func=None):
r = [label]
for d in recent_dates:
v = data.get(d, {}).get(key, "")
if fmt_func:
v = fmt_func(v)
if v == 0 or v == '':
r.append("")
else:
r.append(f"{v}{suffix}")
return r
# 올해 예측 포함 입장객
merged_visitors = ["입장객수"]
for d in recent_dates:
actual = recent_data.get(d, {}).get("입장객 수", 0)
forecast = recent_data.get(d, {}).get("예상 방문자", None)
if d >= today and forecast:
merged_visitors.append(f"{actual} ({int(forecast)})")
else:
merged_visitors.append(actual if actual else "")
year_rows = [
row("홈페이지", "웹 방문자 수", recent_data),
merged_visitors,
row("최저기온", "최저기온", recent_data),
row("최고기온", "최고기온", recent_data),
row("습도", "습도", recent_data, "%"),
row("강수량", "강수량", recent_data),
row("미세먼지지수", "미세먼지", recent_data),
]
for r in year_rows:
ws.append(r)
# 작년 데이터
def prev_row(label, key, suffix="", fmt_func=None):
r = [label]
for d in prev_year_dates:
v = prev_year_data.get(d, {}).get(key, "")
if fmt_func:
v = fmt_func(v)
if v == 0 or v == '':
r.append("")
else:
r.append(f"{v}{suffix}")
return r
prev_rows = [
prev_row("홈페이지", "웹 방문자 수"),
prev_row("입장객수", "입장객 수"),
prev_row("최저기온", "최저기온"),
prev_row("최고기온", "최고기온"),
prev_row("습도", "습도", "%"),
prev_row("강수량", "강수량"),
prev_row("미세먼지지수", "미세먼지"),
]
for r in prev_rows:
ws.append(r)
# 증감 비교
diff = ["입장객 증감"]
rate = ["입장객 변동률"]
temp_dev = ["최고기온 편차"]
for i, d in enumerate(recent_dates):
cur = recent_data.get(d, {}).get("입장객 수", 0)
prev = prev_year_data.get(prev_year_dates[i], {}).get("입장객 수", 0)
if prev:
diff.append(cur - prev)
rate.append(f"{(cur - prev) / prev * 100:.1f}%")
else:
diff.append("")
rate.append("")
t1 = recent_data.get(d, {}).get("최고기온")
t2 = prev_year_data.get(prev_year_dates[i], {}).get("최고기온")
temp_dev.append(round(t1 - t2, 1) if t1 is not None and t2 is not None else "")
for row in [diff, rate, temp_dev]:
ws.append(row)
# 굵은 테두리 처리
for col, d in enumerate(recent_dates, start=2):
if d >= today:
for r in range(data_start_row + 1, data_start_row + 18):
ws.cell(row=r, column=col).border = thick_border
# 차트
chart = LineChart()
chart.title = "입장객 비교 (예상 포함 vs 작년)"
chart.height = 10
chart.width = 22
chart.y_axis.title = ""
chart.x_axis.title = "날짜"
label_ref = Reference(ws, min_col=2, min_row=data_start_row, max_col=1 + len(recent_dates))
this_year_ref = Reference(ws, min_col=2, min_row=data_start_row + 2, max_col=1 + len(recent_dates))
last_year_ref = Reference(ws, min_col=2, min_row=data_start_row + 9, max_col=1 + len(recent_dates))
chart.set_categories(label_ref)
chart.add_data(this_year_ref, titles_from_data=False)
chart.add_data(last_year_ref, titles_from_data=False)
chart.series[0].tx = SeriesLabel(v="입장객수 (예상 포함)")
chart.series[1].tx = SeriesLabel(v="작년 입장객수")
chart.series[1].graphicalProperties.solidFill = "999999"
ws.add_chart(chart, "A1")
wb.save(filename)
print(f"✅ 엑셀 저장 완료: {filename}")