Skip to content

Commit 3dd97b8

Browse files
authored
Update update_readme_calendar.py
1 parent 562fa42 commit 3dd97b8

1 file changed

Lines changed: 66 additions & 102 deletions

File tree

scripts/update_readme_calendar.py

Lines changed: 66 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,11 @@
1414
END_MARK = "<!-- PROGRESS_END -->"
1515
TZ_OFFSET = "+0900" # Asia/Seoul (KST)
1616
WEEK_START = calendar.SUNDAY # 달력 시작: 일요일
17-
DOT_GREEN = "🟢" # 그날 커밋 + 목표 달성
18-
DOT_ORANGE = "🟠" # 다른날 커밋 + 목표 달성
19-
DOT_YELLOW = "🟡" # 커밋 있음 + 목표 미달
20-
DOT_RED = "🔴" # 비봇 커밋 없음
17+
DOT_GREEN = "🟢" # 그날 3커밋 이상 + 누적 3개 이상
18+
DOT_ORANGE = "🟠" # 누적 3개 이상(레트로 달성 포함), 당일 3커밋 미만
19+
DOT_YELLOW = "🟡" # 비봇 커밋은 있으나 누적 < 3
20+
DOT_RED = "🔴" # 비봇 커밋 없음 (표시 n=0)
21+
GOAL_M = 3 # 항상 3
2122
# ==============
2223

2324
calendar.setfirstweekday(WEEK_START)
@@ -26,10 +27,10 @@
2627
def run(cmd):
2728
return subprocess.check_output(cmd, shell=True, text=True, encoding="utf-8").strip()
2829

29-
# ---------- 커밋/메시지 판정 ----------
30+
# ---------- 커밋/메시지 조회 ----------
3031

31-
def git_subjects_for_date_and_path(date_str, path):
32-
"""특정 날짜(KST)의 해당 path 커밋 subject 목록"""
32+
def nonbot_commit_count_on_date(date_str, path):
33+
"""해당 날짜(KST) 동안 path를 건드린 '비봇' 커밋 수"""
3334
since = f"{date_str} 00:00:00 {TZ_OFFSET}"
3435
until = f"{date_str} 23:59:59 {TZ_OFFSET}"
3536
cmd = (
@@ -38,62 +39,38 @@ def git_subjects_for_date_and_path(date_str, path):
3839
)
3940
out = run(cmd)
4041
if not out:
41-
return []
42-
return [line.strip() for line in out.splitlines() if line.strip()]
42+
return 0
43+
cnt = 0
44+
for s in out.splitlines():
45+
s = s.strip()
46+
if s and not BOT_REGEX.match(s):
47+
cnt += 1
48+
return cnt
4349

4450
def latest_nonbot_commit_for_path(path):
4551
"""
46-
해당 path에 대한 '비봇' 커밋 중 가장 최근 항목의 (날짜, 커밋해시)를 반환.
52+
path(예: YYYY-MM-DD/이름)에서 '비봇' 커밋 중 가장 최근의 (날짜, 해시) 반환.
4753
없으면 (None, None).
4854
"""
49-
# 날짜는 KST 로컬 포맷, 해시는 별도로 얻기 위해 %H 추가
5055
cmd = f'git log --pretty="%H%x09%ad%x09%s" --date=format-local:"%Y-%m-%d" -- "{path}" || true'
5156
out = run(cmd)
5257
if not out:
5358
return None, None
5459
for line in out.splitlines():
55-
line = line.strip()
56-
if not line:
60+
parts = line.strip().split("\t", 3)
61+
if len(parts) < 3:
5762
continue
58-
parts = line.split("\t", 2)
59-
if len(parts) != 3:
60-
continue
61-
commit_hash, date_str, subject = parts
62-
subject = subject.strip()
63-
if BOT_REGEX.match(subject):
64-
continue
65-
return date_str, commit_hash
63+
h, date_str, subj = parts[0], parts[1], parts[2].strip()
64+
if not BOT_REGEX.match(subj):
65+
return date_str, h
6666
return None, None
6767

68-
def commit_flag(date_str, name):
69-
"""
70-
커밋 관점 판정:
71-
'O' = 그날 비봇 커밋,
72-
'L' = 다른 날 비봇 커밋,
73-
'X' = 비봇 커밋 없음
74-
"""
75-
path = f"{date_str}/{name}"
76-
subjects_today = git_subjects_for_date_and_path(date_str, path)
77-
for s in subjects_today:
78-
if not BOT_REGEX.match(s):
79-
return "O"
80-
nonbot_date, _ = latest_nonbot_commit_for_path(path)
81-
if nonbot_date is not None and nonbot_date != date_str:
82-
return "L"
83-
return "X"
84-
85-
# ---------- 스냅샷 & 파일 개수 (재귀) ----------
86-
87-
def commit_at_end_of_date(date_str):
88-
"""해당 날짜(KST 23:59:59)의 리포 스냅샷 커밋 해시 반환(없으면 빈 문자열)"""
89-
until = f'{date_str} 23:59:59 {TZ_OFFSET}'
90-
cmd = f'git rev-list -1 --before="{until}" HEAD || true'
91-
return run(cmd).strip()
92-
93-
def count_files_recursive_at_commit(commit, base_dir):
68+
# ---------- 스냅샷 & 파일 개수(재귀, .gitkeep 제외) ----------
69+
70+
def files_excluding_gitkeep_at_commit(commit, base_dir):
9471
"""
95-
특정 커밋에서 base_dir/ 아래의 모든 파일(=blob) 리스트와 .gitkeep 존재 여부 반환.
96-
- 출력: (파일경로목록(list[str]), has_gitkeep(bool))
72+
특정 커밋에서 base_dir/ 아래 모든 파일 목록(.gitkeep 제외)과,
73+
.gitkeep 존재 여부를 함께 반환.
9774
"""
9875
if not commit:
9976
return [], False
@@ -102,54 +79,34 @@ def count_files_recursive_at_commit(commit, base_dir):
10279
out = run(cmd)
10380
if not out:
10481
return [], False
105-
10682
files = []
10783
has_gitkeep = False
10884
for line in out.splitlines():
10985
name = line.strip()
11086
if not name or not name.startswith(base):
11187
continue
112-
files.append(name)
11388
if os.path.basename(name) == ".gitkeep":
11489
has_gitkeep = True
90+
continue # 표시/카운트에서 제외
91+
files.append(name)
11592
return files, has_gitkeep
11693

117-
def snapshot_for_display_and_goal(date_str, name, cf):
94+
def display_total_files_and_has_nonbot(path):
11895
"""
119-
표시/판정에 사용할 '스냅샷 커밋'을 선택하고, 그 스냅샷에서:
120-
- 표시용 파일 개수(display_n): .gitkeep 제외
121-
- 목표값(m): .gitkeep 있으면 4, 없으면 3
122-
- 충족 여부(ok): (표시용이 아니라 실제 전체 파일수 >= m)
123-
반환: (display_n, m, ok)
96+
표시용 누적 파일 수 n 계산:
97+
- path의 '가장 최근 비봇 커밋' 스냅샷에서 .gitkeep 제외 파일 개수
98+
또한 비봇 커밋 존재 여부(boolean)도 함께 반환.
12499
"""
125-
path = f"{date_str}/{name}"
126-
127-
if cf == "O":
128-
# 같은 날: 그날 끝 스냅샷
129-
commit = commit_at_end_of_date(date_str)
130-
elif cf == "L":
131-
# 레트로: 최신 비봇 커밋 스냅샷
132-
_, commit = latest_nonbot_commit_for_path(path)
133-
if not commit:
134-
# 안전망: 없으면 그날 스냅샷 시도
135-
commit = commit_at_end_of_date(date_str)
136-
else: # 'X'
137-
# 비봇 커밋 없으면 그날 스냅샷으로 계산(대개 0)
138-
commit = commit_at_end_of_date(date_str)
139-
140-
files, has_gitkeep = count_files_recursive_at_commit(commit, path)
141-
total_including_gitkeep = len(files)
142-
# 표시용 개수는 .gitkeep 제외
143-
display_n = total_including_gitkeep - (1 if any(os.path.basename(f) == ".gitkeep" for f in files) else 0)
144-
# 목표값 산정
145-
m = 4 if has_gitkeep else 3
146-
ok = total_including_gitkeep >= m
147-
return display_n, m, ok
148-
149-
# ---------- 달력 렌더링 ----------
100+
nonbot_date, nonbot_hash = latest_nonbot_commit_for_path(path)
101+
if not nonbot_hash:
102+
# 비봇 커밋이 전혀 없으면 표시 n=0
103+
return 0, False
104+
files, _ = files_excluding_gitkeep_at_commit(nonbot_hash, path)
105+
return len(files), True
106+
107+
# ---------- 렌더링 로직 ----------
150108

151109
def find_all_date_dirs():
152-
"""리포 내 YYYY-MM-DD 디렉터리들을 찾아 실제 날짜 리스트 반환."""
153110
dates = []
154111
for entry in os.listdir("."):
155112
if re.fullmatch(r"\d{4}-\d{2}-\d{2}", entry) and os.path.isdir(entry):
@@ -161,7 +118,6 @@ def find_all_date_dirs():
161118
return sorted(dates)
162119

163120
def month_iter(start_date, end_date):
164-
"""start_date의 1일 ~ end_date의 1일까지 월 단위 이터레이션."""
165121
y, m = start_date.year, start_date.month
166122
while (y < end_date.year) or (y == end_date.year and m <= end_date.month):
167123
yield y, m
@@ -186,7 +142,6 @@ def build_month_calendar(year, month, today_kst):
186142

187143
date_obj = datetime.date(year, month, d)
188144
if date_obj >= today_kst:
189-
# 오늘/미래 날짜는 빈 칸
190145
tds.append(
191146
f'<td align="center" valign="top">'
192147
f'<div align="right"><sub>{d}</sub></div>'
@@ -197,21 +152,29 @@ def build_month_calendar(year, month, today_kst):
197152
date_str = date_obj.isoformat()
198153
lines = []
199154
for name in NAMES:
200-
cf = commit_flag(date_str, name) # 'O','L','X'
201-
display_n, m, ok = snapshot_for_display_and_goal(date_str, name, cf)
202-
203-
if cf == "O":
204-
dot = DOT_GREEN if ok else DOT_YELLOW
205-
elif cf == "L":
206-
dot = DOT_ORANGE if ok else DOT_YELLOW
155+
path = f"{date_str}/{name}"
156+
157+
# 1) 누적 파일 수 n (레트로 포함), 비봇 커밋 존재 여부
158+
n, has_nonbot = display_total_files_and_has_nonbot(path)
159+
160+
# 2) 당일 비봇 커밋 수
161+
today_nonbot_cnt = nonbot_commit_count_on_date(date_str, path)
162+
163+
# 3) 색상 결정
164+
if not has_nonbot:
165+
dot = DOT_RED # 비봇 커밋 없음 → 빨강, n=0
166+
n = 0
167+
elif n >= GOAL_M:
168+
if today_nonbot_cnt >= GOAL_M:
169+
dot = DOT_GREEN # 당일 3커밋 이상 + 누적 3개 이상
170+
else:
171+
dot = DOT_ORANGE # 누적 3개 이상, 당일 3커밋 미만(레트로 포함 달성)
207172
else:
208-
dot = DOT_RED
209-
# cf == 'X'일 땐 ok 의미가 없지만, 표시 일관성을 위해 (n/m) 그대로 둠.
173+
dot = DOT_YELLOW # 비봇 커밋은 있으나 누적 < 3
210174

211-
# 👉 표시를 심플하게: "이름: 🟡 (n/m)"
175+
# 4) 표시 (이름: 🟢 (n/3)) 단순 형식
212176
lines.append(
213-
f"<div style='font-size:13px'>{name}: {dot} "
214-
f"(<code>{display_n}/{m}</code>)</div>"
177+
f"<div style='font-size:13px'>{name}: {dot} (<code>{n}/{GOAL_M}</code>)</div>"
215178
)
216179

217180
cell_html = (
@@ -226,11 +189,12 @@ def build_month_calendar(year, month, today_kst):
226189
month_title = f"### {year}-{month:02d} 코딩테스트 달력 (KST)"
227190
legend = (
228191
"<sub>"
229-
"🟢=그날 커밋+목표달성, "
230-
"🟠=다른날 커밋+목표달성, "
231-
"🟡=커밋있음+목표미달, "
232-
"🔴=커밋없음 · "
233-
"(표시 n은 .gitkeep 제외)</sub>"
192+
"🟢=그날 3커밋 이상+누적 3개 이상, "
193+
"🟠=누적 3개 이상(레트로 포함) & 당일 3커밋 미만, "
194+
"🟡=커밋은 있으나 누적 < 3, "
195+
"🔴=비봇 커밋 없음 · "
196+
"(표시 n은 .gitkeep 제외, 목표는 항상 3)"
197+
"</sub>"
234198
)
235199
table_html = (
236200
f"{month_title}\n\n"

0 commit comments

Comments
 (0)