Skip to content

Commit 8239a4a

Browse files
authored
Update update_readme_calendar.py
1 parent f4e860b commit 8239a4a

1 file changed

Lines changed: 52 additions & 15 deletions

File tree

scripts/update_readme_calendar.py

Lines changed: 52 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313
START_MARK = "<!-- PROGRESS_START -->"
1414
END_MARK = "<!-- PROGRESS_END -->"
1515
TZ_OFFSET = "+0900" # Asia/Seoul (KST)
16-
WEEK_START = calendar.SUNDAY
17-
DOT_GREEN = "🟢" # 그날 커밋 + 목표 달성
16+
WEEK_START = calendar.SUNDAY # 달력 시작: 일요일
17+
DOT_GREEN = "🟢" # 그날 커밋 + 목표 달성
1818
DOT_ORANGE = "🟠" # 다른날 커밋 + 목표 달성
19-
DOT_YELLOW = "🟡" # 커밋은 있지만 목표 미달
20-
DOT_RED = "🔴" # 커밋 없음
19+
DOT_YELLOW = "🟡" # 커밋 있음 + 목표 미달
20+
DOT_RED = "🔴" # 비봇 커밋 없음
2121
# ==============
2222

2323
calendar.setfirstweekday(WEEK_START)
@@ -29,6 +29,7 @@ def run(cmd):
2929
# ---------- 커밋/메시지 판정 ----------
3030

3131
def 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

4344
def 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

6570
def 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

7989
def 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

8495
def 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

103125
def 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

113139
def 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

124151
def 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):
231264
def 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

Comments
 (0)