Skip to content

Commit 21f9635

Browse files
authored
Update update_readme_calendar.py
1 parent 7fa9b66 commit 21f9635

1 file changed

Lines changed: 69 additions & 16 deletions

File tree

scripts/update_readme_calendar.py

Lines changed: 69 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,20 @@
1414
END_MARK = "<!-- PROGRESS_END -->"
1515
TZ_OFFSET = "+0900" # Asia/Seoul (KST)
1616
WEEK_START = calendar.SUNDAY # 달력 시작: 일요일
17-
DOT_O = "🟢" # 개인 커밋 존재
18-
DOT_X = "🔴" # 봇 커밋만 있거나 없음
17+
DOT_O = "🟢" # 해당 날짜에 비봇 커밋 존재
18+
DOT_L = "🟠" # 해당 날짜엔 없지만 다른 날짜에 비봇 커밋 존재(레트로)
19+
DOT_X = "🔴" # 봇 커밋만 있거나 커밋 없음
1920
# ==============
2021

2122
calendar.setfirstweekday(WEEK_START)
2223

24+
BOT_REGEX = re.compile(r"^\d{4}-\d{2}-\d{2}일자 태스크 배정완료, 화이팅!$")
25+
2326
def run(cmd):
2427
return subprocess.check_output(cmd, shell=True, text=True, encoding="utf-8").strip()
2528

2629
def git_subjects_for_date_and_path(date_str, path):
30+
"""특정 날짜 KST 범위의 커밋 subject들(해당 path에 한정)"""
2731
since = f"{date_str} 00:00:00 {TZ_OFFSET}"
2832
until = f"{date_str} 23:59:59 {TZ_OFFSET}"
2933
cmd = (
@@ -35,20 +39,58 @@ def git_subjects_for_date_and_path(date_str, path):
3539
return []
3640
return [line.strip() for line in out.splitlines() if line.strip()]
3741

42+
def latest_nonbot_commit_date_for_path(path):
43+
"""
44+
해당 path를 건드린 커밋 중 '봇이 아닌' 가장 최근 커밋의 날짜(YYYY-MM-DD, 로컬=KST)를 반환.
45+
없으면 None.
46+
"""
47+
# 워크플로우에서 시스템 타임존을 Asia/Seoul로 설정하므로 --date=format-local 사용
48+
cmd = f'git log --pretty="%ad%x09%s" --date=format-local:"%Y-%m-%d" -- "{path}" || true'
49+
out = run(cmd)
50+
if not out:
51+
return None
52+
for line in out.splitlines():
53+
line = line.strip()
54+
if not line:
55+
continue
56+
try:
57+
date_part, subject = line.split("\t", 1)
58+
except ValueError:
59+
# 탭이 없을 수도 있으니 방어
60+
parts = line.split(" ", 1)
61+
if len(parts) == 2:
62+
date_part, subject = parts
63+
else:
64+
continue
65+
subject = subject.strip()
66+
# 봇 커밋은 건너뛴다(어떤 날짜든)
67+
if BOT_REGEX.match(subject):
68+
continue
69+
# 비봇 커밋이면 그 커밋의 날짜를 반환
70+
return date_part
71+
return None
72+
3873
def judge_day(date_str, name):
3974
"""
40-
반환: 'O' 또는 'X'
41-
- 해당 날짜/이름 경로에 커밋이 없거나 봇 메시지만 => 'X'
42-
- 그 외 메시지 하나라도 => 'O'
75+
반환: 'O' / 'L' / 'X'
76+
- 'O' : 해당 날짜에 비봇 커밋 존재
77+
- 'L' : 해당 날짜엔 없지만, 다른 날짜에 비봇 커밋 존재(레트로)
78+
- 'X' : 봇 커밋만 있거나 커밋 없음
4379
"""
44-
bot_msg = f"{date_str}일자 태스크 배정완료, 화이팅!"
4580
path = f"{date_str}/{name}"
46-
subjects = git_subjects_for_date_and_path(date_str, path)
47-
if not subjects:
48-
return "X"
49-
for s in subjects:
50-
if s != bot_msg:
81+
subjects_today = git_subjects_for_date_and_path(date_str, path)
82+
83+
# 오늘자 범위에서 봇 외 커밋이 하나라도 있으면 O
84+
for s in subjects_today:
85+
if not BOT_REGEX.match(s):
5186
return "O"
87+
88+
# 오늘자엔 없었으나, 과거/미래 다른 날짜에 비봇 커밋이 있으면 L
89+
nonbot_any_date = latest_nonbot_commit_date_for_path(path)
90+
if nonbot_any_date is not None and nonbot_any_date != date_str:
91+
return "L"
92+
93+
# 그 외에는 X
5294
return "X"
5395

5496
def find_all_date_dirs():
@@ -77,7 +119,6 @@ def month_iter(start_date, end_date):
77119
def build_month_calendar(year, month, today_kst):
78120
cal = calendar.monthcalendar(year, month)
79121
header_days = ["일", "월", "화", "수", "목", "금", "토"]
80-
# firstweekday 반영
81122
header_days = header_days[-calendar.firstweekday():] + header_days[:-calendar.firstweekday()]
82123

83124
rows_html = []
@@ -101,12 +142,17 @@ def build_month_calendar(year, month, today_kst):
101142
date_str = date_obj.isoformat()
102143
lines = []
103144
for name in NAMES:
104-
flag = judge_day(date_str, name) # 'O' or 'X'
105-
dot = DOT_O if flag == "O" else DOT_X
145+
flag = judge_day(date_str, name) # 'O' / 'L' / 'X'
146+
if flag == "O":
147+
dot = DOT_O
148+
elif flag == "L":
149+
dot = DOT_L
150+
else:
151+
dot = DOT_X
106152
lines.append(f"<div style='font-size:13px'>{name}: {dot}</div>")
107153

108154
cell_html = (
109-
'<td align="center" valign="top" style="min-width:140px">'
155+
'<td align="center" valign="top" style="min-width:150px">'
110156
f'<div align="right"><sub>{d}</sub></div>'
111157
+ "".join(lines) +
112158
"</td>"
@@ -115,8 +161,16 @@ def build_month_calendar(year, month, today_kst):
115161
rows_html.append("<tr>" + "".join(tds) + "</tr>")
116162

117163
month_title = f"### {year}-{month:02d} 코딩테스트 달력 (KST)"
164+
legend = (
165+
"<sub>"
166+
"🟢=당일 비봇 커밋, "
167+
"🟠=다른 날 비봇 커밋(레트로), "
168+
"🔴=봇만/없음"
169+
"</sub>"
170+
)
118171
table_html = (
119172
f"{month_title}\n\n"
173+
+ legend + "\n\n"
120174
+ '<table>'
121175
+ "<thead><tr>" + "".join([f"<th>{d}</th>" for d in header_days]) + "</tr></thead>"
122176
+ "<tbody>" + "".join(rows_html) + "</tbody>"
@@ -135,7 +189,6 @@ def build_all_months(today_kst):
135189
blocks = []
136190
for y, m in month_iter(start, end):
137191
block = build_month_calendar(y, m, today_kst)
138-
# 이번 달은 기본 펼침, 과거 달은 접기
139192
is_current = (y == today_kst.year and m == today_kst.month)
140193
summary = f"{y}-{m:02d}"
141194
details_open = " open" if is_current else ""

0 commit comments

Comments
 (0)