1313START_MARK = "<!-- PROGRESS_START -->"
1414END_MARK = "<!-- PROGRESS_END -->"
1515TZ_OFFSET = "+0900" # Asia/Seoul (KST)
16- WEEK_START = calendar .SUNDAY
17- DOT_GREEN = "🟢" # 그날 커밋 + 목표 달성
16+ WEEK_START = calendar .SUNDAY # 달력 시작: 일요일
17+ DOT_GREEN = "🟢" # 그날 커밋 + 목표 달성
1818DOT_ORANGE = "🟠" # 다른날 커밋 + 목표 달성
19- DOT_YELLOW = "🟡" # 커밋은 있지만 목표 미달
20- DOT_RED = "🔴" # 커밋 없음
19+ DOT_YELLOW = "🟡" # 커밋 있음 + 목표 미달
20+ DOT_RED = "🔴" # 비봇 커밋 없음
2121# ==============
2222
2323calendar .setfirstweekday (WEEK_START )
@@ -29,6 +29,7 @@ def run(cmd):
2929# ---------- 커밋/메시지 판정 ----------
3030
3131def git_subjects_for_date_and_path (date_str , path ):
32+ """특정 날짜(KST)의 해당 path 커밋 subject 목록"""
3233 since = f"{ date_str } 00:00:00 { TZ_OFFSET } "
3334 until = f"{ date_str } 23:59:59 { TZ_OFFSET } "
3435 cmd = (
@@ -41,6 +42,10 @@ def git_subjects_for_date_and_path(date_str, path):
4142 return [line .strip () for line in out .splitlines () if line .strip ()]
4243
4344def latest_nonbot_commit_date_for_path (path ):
45+ """
46+ 해당 path에 대한 '비봇' 커밋 중 가장 최근 커밋의 날짜(YYYY-MM-DD, KST 기준)를 반환.
47+ 없으면 None.
48+ """
4449 cmd = f'git log --pretty="%ad%x09%s" --date=format-local:"%Y-%m-%d" -- "{ path } " || true'
4550 out = run (cmd )
4651 if not out :
@@ -63,7 +68,12 @@ def latest_nonbot_commit_date_for_path(path):
6368 return None
6469
6570def commit_flag (date_str , name ):
66- """커밋 유무 판정: 'O'=그날 비봇, 'L'=다른날 비봇, 'X'=없음"""
71+ """
72+ 커밋 관점 판정:
73+ 'O' = 그날 비봇 커밋,
74+ 'L' = 다른 날 비봇 커밋,
75+ 'X' = 비봇 커밋 없음
76+ """
6777 path = f"{ date_str } /{ name } "
6878 subjects_today = git_subjects_for_date_and_path (date_str , path )
6979 for s in subjects_today :
@@ -74,33 +84,49 @@ def commit_flag(date_str, name):
7484 return "L"
7585 return "X"
7686
77- # ---------- 파일 개수 ----------
87+ # ---------- 파일 개수(그 날짜 스냅샷) ----------
7888
7989def commit_at_end_of_date (date_str ):
90+ """해당 날짜(KST 23:59:59)의 리포 스냅샷 커밋 해시 반환(없으면 빈 문자열)"""
8091 until = f'{ date_str } 23:59:59 { TZ_OFFSET } '
8192 cmd = f'git rev-list -1 --before="{ until } " HEAD || true'
8293 return run (cmd ).strip ()
8394
8495def file_count_in_path_at_commit (commit , path ):
96+ """
97+ 특정 커밋에서 path/ 디렉터리 '바로 아래' 파일(=blob) 개수와 .gitkeep 포함 여부 반환.
98+ 재귀로 전체 파일을 받은 뒤, base 바로 아래만 필터링한다.
99+ """
85100 if not commit :
86101 return 0 , False
87- cmd = f'git ls-tree { commit } "{ path } " || true'
102+
103+ base = path .rstrip ("/" ) + "/"
104+ # 재귀로 모든 파일 경로를 받고, base 바로 아래만 카운트
105+ cmd = f'git ls-tree -r --name-only { commit } -- "{ base } " || true'
88106 out = run (cmd )
89107 if not out :
90108 return 0 , False
109+
91110 count = 0
92111 has_gitkeep = False
93112 for line in out .splitlines ():
94- parts = line .split ("\t " , 1 )
95- meta = parts [0 ]
96- name = parts [1 ] if len (parts ) > 1 else ""
97- if " blob " in meta :
98- count += 1
99- if os .path .basename (name ) == ".gitkeep" :
100- has_gitkeep = True
113+ name = line .strip ()
114+ if not name .startswith (base ):
115+ continue
116+ rest = name [len (base ):] # base 이후
117+ if "/" in rest :
118+ # 하위 디렉터리 내부는 제외 (바로 아래만 카운트)
119+ continue
120+ count += 1
121+ if rest == ".gitkeep" :
122+ has_gitkeep = True
101123 return count , has_gitkeep
102124
103125def file_req_and_status (date_str , name ):
126+ """
127+ (파일개수, 목표개수, 충족여부) 반환.
128+ 목표: .gitkeep 있으면 4, 없으면 3 (해당 날짜 23:59:59 KST 스냅샷 기준)
129+ """
104130 commit = commit_at_end_of_date (date_str )
105131 path = f"{ date_str } /{ name } "
106132 cnt , has_gitkeep = file_count_in_path_at_commit (commit , path )
@@ -111,6 +137,7 @@ def file_req_and_status(date_str, name):
111137# ---------- 달력 렌더링 ----------
112138
113139def find_all_date_dirs ():
140+ """리포 내 YYYY-MM-DD 디렉터리들을 찾아 실제 날짜 리스트 반환."""
114141 dates = []
115142 for entry in os .listdir ("." ):
116143 if re .fullmatch (r"\d{4}-\d{2}-\d{2}" , entry ) and os .path .isdir (entry ):
@@ -122,6 +149,7 @@ def find_all_date_dirs():
122149 return sorted (dates )
123150
124151def month_iter (start_date , end_date ):
152+ """start_date의 1일 ~ end_date의 1일까지 월 단위 이터레이션."""
125153 y , m = start_date .year , start_date .month
126154 while (y < end_date .year ) or (y == end_date .year and m <= end_date .month ):
127155 yield y , m
@@ -143,8 +171,10 @@ def build_month_calendar(year, month, today_kst):
143171 if d == 0 :
144172 tds .append ("<td></td>" )
145173 continue
174+
146175 date_obj = datetime .date (year , month , d )
147176 if date_obj >= today_kst :
177+ # 오늘/미래 날짜는 빈 칸
148178 tds .append (
149179 f'<td align="center" valign="top">'
150180 f'<div align="right"><sub>{ d } </sub></div>'
@@ -155,9 +185,11 @@ def build_month_calendar(year, month, today_kst):
155185 date_str = date_obj .isoformat ()
156186 lines = []
157187 for name in NAMES :
188+ # 커밋/파일 판정
158189 cf = commit_flag (date_str , name ) # 'O','L','X'
159190 cnt , req , ok = file_req_and_status (date_str , name )
160191
192+ # 색상 결정
161193 if cf == "O" :
162194 dot = DOT_GREEN if ok else DOT_YELLOW
163195 elif cf == "L" :
@@ -187,7 +219,7 @@ def build_month_calendar(year, month, today_kst):
187219 "🟠=다른날 커밋+목표달성, "
188220 "🟡=커밋있음+목표미달, "
189221 "🔴=커밋없음 · "
190- "<code>n/m</code>=파일개수/목표"
222+ "<code>n/m</code>=파일개수/목표(.gitkeep 있으면 m=4, 없으면 m=3) "
191223 "</sub>"
192224 )
193225 table_html = (
@@ -206,6 +238,7 @@ def build_all_months(today_kst):
206238 start = datetime .date (date_dirs [0 ].year , date_dirs [0 ].month , 1 )
207239 else :
208240 start = datetime .date (today_kst .year , today_kst .month , 1 )
241+
209242 end = datetime .date (today_kst .year , today_kst .month , 1 )
210243 blocks = []
211244 for y , m in month_iter (start , end ):
@@ -231,13 +264,17 @@ def replace_block(original, new_block):
231264def main ():
232265 now = datetime .datetime .now (datetime .timezone (datetime .timedelta (hours = 9 )))
233266 today_kst = now .date ()
267+
234268 new_block = build_all_months (today_kst )
269+
235270 if os .path .exists (READ_ME ):
236271 with open (READ_ME , "r" , encoding = "utf-8" ) as f :
237272 content = f .read ()
238273 else :
239274 content = "# 코딩테스트 연습\n "
275+
240276 updated = replace_block (content , new_block )
277+
241278 if updated != content :
242279 with open (READ_ME , "w" , encoding = "utf-8" ) as f :
243280 f .write (updated )
0 commit comments