Skip to content

Commit e878084

Browse files
committed
feat: 챌린지 유형 진행 상황을 자동화하기 위한 코드 추가
1 parent 00a9824 commit e878084

File tree

5 files changed

+262
-0
lines changed

5 files changed

+262
-0
lines changed

_MonthlyChallenges/DASHBOARD.md

Whitespace-only changes.

_MonthlyChallenges/HISTORY.md

Whitespace-only changes.
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
import os
2+
import json
3+
import re
4+
5+
6+
def main():
7+
# 1. GitHub 이벤트 페이로드 로드
8+
event_path = os.getenv('GITHUB_EVENT_PATH')
9+
print("event_path", event_path)
10+
if not event_path:
11+
print("GITHUB_EVENT_PATH 환경변수가 설정되어 있지 않습니다.")
12+
exit(1)
13+
14+
with open(event_path, 'r', encoding='utf-8') as f:
15+
event_data = json.load(f)
16+
print(f"event_data: {event_data}")
17+
18+
pr_body = event_data['pull_request']['body']
19+
pr_number = event_data['pull_request']['number']
20+
username = event_data['pull_request']['user']['login']
21+
print(f"pr_body: {pr_body}, pr_number: {pr_number}")
22+
23+
if pr_number is None:
24+
print("PR 번호를 찾을 수 없습니다.")
25+
exit(1)
26+
27+
# 2. PR 본문을 줄 단위로 분할
28+
lines = pr_body.splitlines()
29+
print(f"lines: {lines}")
30+
31+
# 3. "푼 문제" 섹션 탐지 및 문제 항목 파싱
32+
in_problem_section = False
33+
problem_entries = []
34+
35+
# 정규표현식 패턴:
36+
# - [백준 #2169. 로봇 조종하기](https://www.acmicpc.net/problem/2169): DP / 골드2
37+
pattern = r'- \[(?P<source>[^\]]+?) #(?P<id>\d+)\.\s+(?P<title>.*?)\]\((?P<link>.*?)\):\s*(?P<algorithm>[^/]+)\s*/\s*(?P<difficulty>.+)'
38+
39+
for line in lines:
40+
# "푼 문제" 키워드를 찾으면 해당 섹션 시작으로 설정
41+
if "푼 문제" in line:
42+
in_problem_section = True
43+
continue
44+
45+
if in_problem_section:
46+
# bullet list 항목만 처리
47+
if line.startswith('- '):
48+
match = re.match(pattern, line)
49+
print(f"match: {match}")
50+
if match:
51+
entry = match.groupdict()
52+
print(f"entry: {entry}")
53+
# 문제 번호를 정수로 변환
54+
entry['problem_id'] = int(entry.pop("id"))
55+
entry['pr_number'] = pr_number
56+
entry['username'] = username
57+
# 공백 제거
58+
entry['algorithm'] = entry['algorithm'].strip()
59+
entry['difficulty'] = entry['difficulty'].strip()
60+
problem_entries.append(entry)
61+
print(f"problem_entries: {problem_entries}")
62+
63+
# 4. 추출된 데이터를 pr_data.json 파일에 저장
64+
with open("pr_data.json","w", encoding='utf-8') as f:
65+
json.dump(problem_entries, f, ensure_ascii=False, indent=2)
66+
67+
if __name__ == '__main__':
68+
main()
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
import os
2+
import json
3+
from datetime import datetime
4+
5+
SCOREBOARD_FILE = "scoreboard.json"
6+
DASHBOARD_FILE = "DASHBOARD.md"
7+
HISTORY_FILE = "HISTORY.md"
8+
9+
CHALLENGE_TYPES = {
10+
"그래프": 5,
11+
"DP": 5
12+
}
13+
14+
def generate_dashboard(scoreboard):
15+
# scoreboard 구조 예시 (새로운 형식):
16+
# {
17+
# "month": "2023-04",
18+
# "users": {
19+
# "user1": {
20+
# "그래프": [101, 102],
21+
# "DP": [2169, 1520],
22+
# "achieved": {
23+
# "그래프": true,
24+
# "DP": false
25+
# }
26+
# },
27+
# "user2": { ... }
28+
# }
29+
# }
30+
month = scoreboard.get("month", "Unknown")
31+
users = scoreboard.get("users", {})
32+
33+
md = f"# {month} 챌린지 진행 상황\n\n"
34+
md += "| 사용자 | 챌린지 유형 | 문제 수 | 달성 여부 |\n"
35+
md += "| ------ | ----------- | ------- | --------- |\n"
36+
37+
# 각 사용자별 진행 상황 표 작성
38+
for user, data in users.items():
39+
for ctype in CHALLENGE_TYPES.keys():
40+
count = len(data.get(ctype, []))
41+
achieved = data.get("achieved", {}).get(ctype, False)
42+
achieved_str = "✅" if achieved else "❌"
43+
md += f"| {user} | {ctype} | {count} | {achieved_str} |\n"
44+
45+
return md
46+
47+
def archive_current_month(scoreboard):
48+
# HISTORY_FILE에 현재 달 기록을 추가 (append 방식)
49+
dashboard_md = generate_dashboard(scoreboard)
50+
with open(HISTORY_FILE, "a", encoding="utf-8") as f:
51+
f.write(dashboard_md)
52+
f.write("\n\n") # 구분을 위한 빈 줄 추가
53+
print("HISTORY.md 업데이트 완료!")
54+
55+
def update_dashboard():
56+
# 1. scoreboard.json 로드
57+
if not os.path.exists(SCOREBOARD_FILE):
58+
print(f"{SCOREBOARD_FILE} 파일이 없습니다.")
59+
return
60+
61+
with open(SCOREBOARD_FILE, "r", encoding="utf-8") as f:
62+
scoreboard = json.load(f)
63+
64+
# 2. 기존 파일 구조가 새 형식("month", "users")이 아니라면 변환
65+
if "month" not in scoreboard or "users" not in scoreboard:
66+
# 기존 구조는 사용자 이름이 최상위 키인 형태
67+
scoreboard = {
68+
"month": datetime.now().strftime("%Y-%m"),
69+
"users": scoreboard
70+
}
71+
# 새 형식으로 저장
72+
with open(SCOREBOARD_FILE, "w", encoding="utf-8") as f:
73+
json.dump(scoreboard, f, ensure_ascii=False, indent=2)
74+
print("기존 scoreboard 형식을 새 구조로 변환하였습니다.")
75+
76+
# 3. 현재 달 확인 및 월 초기화 처리
77+
current_month = datetime.now().strftime("%Y-%m")
78+
stored_month = scoreboard.get("month", current_month)
79+
print(f"현재 달: {current_month}, 저장된 달: {stored_month}")
80+
81+
if stored_month != current_month:
82+
print(f"새로운 달({current_month})로 넘어감 - 이전 달({stored_month}) 기록을 히스토리에 저장합니다.")
83+
archive_current_month(scoreboard)
84+
# scoreboard 초기화: users는 빈 dict, month는 현재 달로 갱신
85+
scoreboard = {
86+
"month": current_month,
87+
"users": {}
88+
}
89+
with open(SCOREBOARD_FILE, "w", encoding="utf-8") as f:
90+
json.dump(scoreboard, f, ensure_ascii=False, indent=2)
91+
92+
# 4. DASHBOARD.md 파일 생성 및 업데이트
93+
md_content = generate_dashboard(scoreboard)
94+
with open(DASHBOARD_FILE, "w", encoding="utf-8") as f:
95+
f.write(md_content)
96+
97+
print("DASHBOARD.md 업데이트 완료!")
98+
99+
if __name__ == '__main__':
100+
update_dashboard()
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import os
2+
import json
3+
from datetime import datetime
4+
5+
SCOREBOARD_FILE = "scoreboard.json"
6+
PR_DATA_FILE = "pr_data.json"
7+
8+
# 챌린지 설정 (매달 투표 결과로 정해진 유형과 목표 문제 수)
9+
CHALLENGE_TYPES = {
10+
"그래프": 5,
11+
"DP": 5
12+
}
13+
14+
15+
def initialize_user():
16+
# 사용자별 초기 스코어보드 구조
17+
return {
18+
**{ctype: [] for ctype in CHALLENGE_TYPES.keys()},
19+
"achieved": {ctype: False for ctype in CHALLENGE_TYPES.keys()}
20+
}
21+
22+
23+
def main():
24+
# 1. 기존 스코어보드 로드 (없으면 빈 dict로 초기화)
25+
if os.path.exists(SCOREBOARD_FILE):
26+
with open(SCOREBOARD_FILE, 'r', encoding='utf-8') as f:
27+
try:
28+
scoreboard = json.load(f)
29+
except json.JSONDecodeError:
30+
scoreboard = {}
31+
else:
32+
scoreboard = {}
33+
34+
print(f"scorebard: {scoreboard}")
35+
36+
# 2. 새 구조("month", "users")가 없다면 변환
37+
if "users" not in scoreboard or "month" not in scoreboard:
38+
scoreboard = {
39+
"month": datetime.now().strftime("%Y-%m"),
40+
"users": scoreboard # 기존 scoreboard의 내용(사용자 데이터)이 있다면 여기에 넣음
41+
}
42+
43+
users = scoreboard["users"]
44+
45+
# 3. pr_data.json 파일 로드
46+
if not os.path.exists(PR_DATA_FILE):
47+
print(f"{PR_DATA_FILE} 파일이 존재하지 않습니다.")
48+
exit(1)
49+
50+
with open(PR_DATA_FILE, 'r', encoding='utf-8') as f:
51+
pr_data = json.load(f)
52+
53+
print(f"pr_data: {pr_data}")
54+
55+
# 4. pr_data의 각 항목을 순회하며 사용자별 스코어보드 업데이트
56+
for entry in pr_data:
57+
username = entry["username"]
58+
algorithm = entry["algorithm"]
59+
problem_id = entry["problem_id"]
60+
61+
if not username or not algorithm or problem_id is None:
62+
continue
63+
64+
print(f"username: {username}, algorithm: {algorithm}, problem_id: {problem_id}")
65+
66+
# 챌린지 유형에 포함되어 있는지 확인 (예: "그래프", "DP")
67+
if algorithm not in CHALLENGE_TYPES:
68+
continue # 챌린지 대상이 아니면 무시
69+
70+
# 사용자의 기록이 없으면 초기화
71+
if username not in users:
72+
scoreboard[username] = initialize_user()
73+
74+
# 해당 유형 문제 번호를 중복 없이 추가
75+
if problem_id not in users[username].get(algorithm, []):
76+
users[username][algorithm].append(problem_id)
77+
78+
print(f"users: {users}")
79+
80+
# 5. 각 사용자별로 달성 여부 업데이트
81+
for username, data in users.items():
82+
for ctype, goal in CHALLENGE_TYPES.items():
83+
count = len(data.get(ctype, []))
84+
# 목표 수 이상이면 달성 처리
85+
data["achieved"][ctype] = (count >= goal)
86+
87+
# 5. 스코어보드 저장
88+
with open(SCOREBOARD_FILE, 'w', encoding='utf-8') as f:
89+
json.dump(scoreboard, f, ensure_ascii=False, indent=2)
90+
91+
print("scoreboard.json 업데이트 완료!")
92+
93+
if __name__ == '__main__':
94+
main()

0 commit comments

Comments
 (0)