1414END_MARK = "<!-- PROGRESS_END -->"
1515TZ_OFFSET = "+0900" # Asia/Seoul (KST)
1616WEEK_START = calendar .SUNDAY # 달력 시작: 일요일
17- DOT_O = "🟢" # 개인 커밋 존재
18- DOT_X = "🔴" # 봇 커밋만 있거나 없음
17+ DOT_O = "🟢" # 해당 날짜에 비봇 커밋 존재
18+ DOT_L = "🟠" # 해당 날짜엔 없지만 다른 날짜에 비봇 커밋 존재(레트로)
19+ DOT_X = "🔴" # 봇 커밋만 있거나 커밋 없음
1920# ==============
2021
2122calendar .setfirstweekday (WEEK_START )
2223
24+ BOT_REGEX = re .compile (r"^\d{4}-\d{2}-\d{2}일자 태스크 배정완료, 화이팅!$" )
25+
2326def run (cmd ):
2427 return subprocess .check_output (cmd , shell = True , text = True , encoding = "utf-8" ).strip ()
2528
2629def 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+
3873def 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
5496def find_all_date_dirs ():
@@ -77,7 +119,6 @@ def month_iter(start_date, end_date):
77119def 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