feat(cupang): 달력 칸에 공휴일 이름 표시(대체공휴일 포함)
- holidays.py 를 이름까지 담도록 확장(holiday_name), is_holiday 는 이름 유무로 판정 - 대체공휴일은 원래 명칭 대신 "대체공휴일"로 표기, 겹치는 날은 "어린이날·부처님오신날" - 달력 칸에 날짜 아래 빨강 소형 라벨 + title 툴팁, 칸 최소 높이 62→70px Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
"""대한민국 공휴일 판정 (달력 색상용).
|
||||
"""대한민국 공휴일 판정 + 이름 (달력 색상·표기용).
|
||||
|
||||
- 고정 양력 공휴일은 매년 동일 → 연도 무관 판정.
|
||||
- 음력 공휴일(설날/부처님오신날/추석)과 대체공휴일은 매년 달라짐 →
|
||||
연도별 dict(`_LUNAR_AND_SUBSTITUTE`)에 명시. 새 연도는 KASI 발표값을 추가한다.
|
||||
- 대체공휴일은 원래 공휴일 이름 대신 "대체공휴일"로 표기한다.
|
||||
|
||||
미수록 연도는 고정 양력 공휴일만 빨강 처리된다(음력/대체는 누락).
|
||||
"""
|
||||
@@ -11,52 +12,78 @@ from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
|
||||
# 매년 동일한 양력 공휴일 (month, day)
|
||||
_FIXED_SOLAR: set[tuple[int, int]] = {
|
||||
(1, 1), # 신정
|
||||
(3, 1), # 삼일절
|
||||
(5, 5), # 어린이날
|
||||
(6, 6), # 현충일
|
||||
(8, 15), # 광복절
|
||||
(10, 3), # 개천절
|
||||
(10, 9), # 한글날
|
||||
(12, 25), # 성탄절
|
||||
SUBSTITUTE = "대체공휴일"
|
||||
|
||||
# 매년 동일한 양력 공휴일 (month, day) -> 이름
|
||||
_FIXED_SOLAR: dict[tuple[int, int], str] = {
|
||||
(1, 1): "신정",
|
||||
(3, 1): "삼일절",
|
||||
(5, 5): "어린이날",
|
||||
(6, 6): "현충일",
|
||||
(8, 15): "광복절",
|
||||
(10, 3): "개천절",
|
||||
(10, 9): "한글날",
|
||||
(12, 25): "성탄절",
|
||||
}
|
||||
|
||||
# 연도별 음력 공휴일 + 대체공휴일 (ISO 날짜 문자열). KASI 발표 기준.
|
||||
_LUNAR_AND_SUBSTITUTE: dict[int, set[str]] = {
|
||||
# 연도별 음력 공휴일 + 대체공휴일 (ISO 날짜 -> 이름). KASI 발표 기준.
|
||||
_LUNAR_AND_SUBSTITUTE: dict[int, dict[str, str]] = {
|
||||
2025: {
|
||||
"2025-01-28", "2025-01-29", "2025-01-30", # 설날 연휴
|
||||
"2025-03-03", # 삼일절 대체(3/1 토)
|
||||
"2025-05-06", # 부처님오신날 대체(5/5 겹침)
|
||||
"2025-05-05", # 부처님오신날(어린이날과 동일일)
|
||||
"2025-10-06", "2025-10-07", "2025-10-08", # 추석 연휴
|
||||
"2025-10-08", # 추석 대체 가능
|
||||
"2025-01-28": "설날 연휴",
|
||||
"2025-01-29": "설날",
|
||||
"2025-01-30": "설날 연휴",
|
||||
"2025-03-03": SUBSTITUTE, # 삼일절(3/1 토)
|
||||
"2025-05-05": "부처님오신날", # 어린이날과 같은 날
|
||||
"2025-05-06": SUBSTITUTE, # 부처님오신날 겹침
|
||||
"2025-10-06": "추석",
|
||||
"2025-10-07": "추석 연휴",
|
||||
"2025-10-08": SUBSTITUTE, # 추석 연휴 겹침
|
||||
},
|
||||
2026: {
|
||||
"2026-02-16", "2026-02-17", "2026-02-18", # 설날 연휴 (설날 2/17)
|
||||
"2026-03-02", # 삼일절 대체 (3/1 일)
|
||||
"2026-05-24", # 부처님오신날 (일)
|
||||
"2026-05-25", # 부처님오신날 대체
|
||||
"2026-08-17", # 광복절 대체 (8/15 토)
|
||||
"2026-09-24", "2026-09-25", "2026-09-26", # 추석 연휴 (추석 9/25)
|
||||
"2026-09-28", # 추석 대체 (9/26 토)
|
||||
"2026-10-05", # 개천절 대체 (10/3 토)
|
||||
"2026-02-16": "설날 연휴",
|
||||
"2026-02-17": "설날",
|
||||
"2026-02-18": "설날 연휴",
|
||||
"2026-03-02": SUBSTITUTE, # 삼일절(3/1 일)
|
||||
"2026-05-24": "부처님오신날",
|
||||
"2026-05-25": SUBSTITUTE,
|
||||
"2026-08-17": SUBSTITUTE, # 광복절(8/15 토)
|
||||
"2026-09-24": "추석 연휴",
|
||||
"2026-09-25": "추석",
|
||||
"2026-09-26": "추석 연휴",
|
||||
"2026-09-28": SUBSTITUTE,
|
||||
"2026-10-05": SUBSTITUTE, # 개천절(10/3 토)
|
||||
},
|
||||
2027: {
|
||||
"2027-02-06", "2027-02-07", "2027-02-08", # 설날 연휴 (설날 2/7)
|
||||
"2027-02-09", # 설날 대체 (2/7 일)
|
||||
"2027-05-13", # 부처님오신날 (목)
|
||||
"2027-08-16", # 광복절 대체 (8/15 일)
|
||||
"2027-09-14", "2027-09-15", "2027-09-16", # 추석 연휴 (추석 9/15)
|
||||
"2027-10-04", # 개천절 대체 (10/3 일)
|
||||
"2027-10-11", # 한글날 대체 (10/9 토)
|
||||
"2027-02-06": "설날 연휴",
|
||||
"2027-02-07": "설날",
|
||||
"2027-02-08": "설날 연휴",
|
||||
"2027-02-09": SUBSTITUTE,
|
||||
"2027-05-13": "부처님오신날",
|
||||
"2027-08-16": SUBSTITUTE, # 광복절(8/15 일)
|
||||
"2027-09-14": "추석 연휴",
|
||||
"2027-09-15": "추석",
|
||||
"2027-09-16": "추석 연휴",
|
||||
"2027-10-04": SUBSTITUTE, # 개천절(10/3 일)
|
||||
"2027-10-11": SUBSTITUTE, # 한글날(10/9 토)
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def holiday_name(d: date) -> str:
|
||||
"""그날의 공휴일 이름. 공휴일이 아니면 빈 문자열.
|
||||
|
||||
한 날에 두 공휴일이 겹치면 "어린이날·부처님오신날" 처럼 이어 붙인다.
|
||||
"""
|
||||
names: list[str] = []
|
||||
fixed = _FIXED_SOLAR.get((d.month, d.day))
|
||||
if fixed:
|
||||
names.append(fixed)
|
||||
extra = _LUNAR_AND_SUBSTITUTE.get(d.year, {}).get(d.isoformat())
|
||||
if extra and extra not in names:
|
||||
names.append(extra)
|
||||
return "·".join(names)
|
||||
|
||||
|
||||
def is_holiday(d: date) -> bool:
|
||||
"""공휴일(일요일 제외)이면 True. 일/토 색상은 요일로 따로 판정한다."""
|
||||
if (d.month, d.day) in _FIXED_SOLAR:
|
||||
return True
|
||||
return d.isoformat() in _LUNAR_AND_SUBSTITUTE.get(d.year, set())
|
||||
return bool(holiday_name(d))
|
||||
|
||||
@@ -21,7 +21,7 @@ from app.timezone import today_kst
|
||||
from fastapi import APIRouter, Body, Depends, File, Form, HTTPException, Request, UploadFile
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
|
||||
from .holidays import is_holiday
|
||||
from .holidays import holiday_name, is_holiday
|
||||
from .store import SHIP_METHODS
|
||||
|
||||
router = APIRouter(prefix="/cupang", tags=["cupang"])
|
||||
@@ -181,6 +181,7 @@ async def index(request: Request) -> HTMLResponse:
|
||||
"is_sunday": d.weekday() == 6,
|
||||
"is_saturday": d.weekday() == 5,
|
||||
"is_holiday": is_holiday(d),
|
||||
"holiday_name": holiday_name(d),
|
||||
"counts": counts.get(d.isoformat(), {}),
|
||||
}
|
||||
for d in week
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903h" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903i" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903h" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903i" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903h" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903i" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
@@ -56,8 +56,11 @@
|
||||
{% if cell.counts.ship %}has-ship{% endif %}
|
||||
{% if cell.is_sunday or cell.is_holiday %}is-red{% elif cell.is_saturday %}is-blue{% endif %}"
|
||||
href="/cupang/?year={{ year }}&month={{ month }}&date={{ cell.date }}"
|
||||
title="{{ cell.date }}{% if cell.counts.ship %} — 출고 {{ cell.counts.ship }}건 / 센터 {{ cell.counts.centers }}곳{% endif %}">
|
||||
title="{{ cell.date }}{% if cell.holiday_name %} — {{ cell.holiday_name }}{% endif %}{% if cell.counts.ship %} — 출고 {{ cell.counts.ship }}건 / 센터 {{ cell.counts.centers }}곳{% endif %}">
|
||||
<span class="cpg-mcal-day">{{ cell.day }}</span>
|
||||
{% if cell.holiday_name %}
|
||||
<span class="cpg-mcal-holi">{{ cell.holiday_name }}</span>
|
||||
{% endif %}
|
||||
{% if cell.counts.ship %}
|
||||
<span class="cpg-mcal-tag">{{ cell.counts.ship }}건<em>{{ cell.counts.centers }}곳</em></span>
|
||||
{% endif %}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903h" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903i" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<section class="cpg">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{% extends "erp_base.html" %}
|
||||
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903h" />{% endblock %}
|
||||
{% block head_extra %}<link rel="stylesheet" href="/static/cupang.css?v=20260903i" />{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{# 출고 묶음 보기 — 상자 계산 화면과 같은 3열 구성. 읽기 전용(수정 없음). #}
|
||||
|
||||
Reference in New Issue
Block a user