22import re
33import requests
44from bs4 import BeautifulSoup
5- import certifi # SSL 검증용
5+ import certifi
66
77ROOT_DIR = "."
88VALID_EXT = (".py" , ".cpp" , ".java" , ".kt" , ".js" )
99
10- # -------------------------------
10+ # -----------------------------
1111# 난이도 정렬 기준 (백준)
12- # -------------------------------
12+ # -----------------------------
1313BOJ_LEVEL_RANK = {
1414 "Bronze V" : 1 , "Bronze IV" : 2 , "Bronze III" : 3 , "Bronze II" : 4 , "Bronze I" : 5 ,
1515 "Silver V" : 6 , "Silver IV" : 7 , "Silver III" : 8 , "Silver II" : 9 , "Silver I" : 10 ,
1919 "Ruby V" : 26 , "Ruby IV" : 27 , "Ruby III" : 28 , "Ruby II" : 29 , "Ruby I" : 30 ,
2020}
2121
22- # -------------------------------
23- # 백준 API
24- # -------------------------------
22+ # -----------------------------
23+ # 백준 정보 가져오기
24+ # -----------------------------
2525def fetch_boj_info (number ):
2626 url = f"https://solved.ac/api/v3/problem/show?problemId={ number } "
2727 try :
2828 r = requests .get (url , timeout = 10 , verify = certifi .where ())
2929 if r .status_code != 200 :
3030 return None
31- except requests .exceptions .RequestException :
31+ data = r .json ()
32+ level_name = [k for k , v in BOJ_LEVEL_RANK .items () if v == data .get ("level" , 0 )]
33+ level_name = level_name [0 ] if level_name else "UNKNOWN"
34+ return {"title" : data .get ("titleKo" , "UNKNOWN" ), "level" : level_name , "link" : f"https://www.acmicpc.net/problem/{ number } " }
35+ except Exception :
3236 return None
3337
34- data = r .json ()
35- level = data .get ("level" , 0 )
36- title = data .get ("titleKo" , "UNKNOWN" )
37-
38- level_name = [k for k , v in BOJ_LEVEL_RANK .items () if v == level ]
39- level_name = level_name [0 ] if level_name else "UNKNOWN"
40-
41- return {"title" : title , "level" : level_name , "link" : f"https://www.acmicpc.net/problem/{ number } " }
42-
43- # -------------------------------
44- # 프로그래머스
45- # -------------------------------
38+ # -----------------------------
39+ # 프로그래머스 정보 가져오기
40+ # -----------------------------
4641def fetch_programmers_info (pid ):
4742 url = f"https://school.programmers.co.kr/learn/courses/30/lessons/{ pid } "
4843 try :
4944 r = requests .get (url , timeout = 10 , verify = certifi .where ())
5045 if r .status_code != 200 :
5146 return None
52- except requests . exceptions . RequestException :
47+ except Exception :
5348 return None
5449
5550 soup = BeautifulSoup (r .text , "html.parser" )
@@ -61,9 +56,9 @@ def fetch_programmers_info(pid):
6156
6257 return {"title" : title , "level" : level , "link" : url }
6358
64- # -------------------------------
59+ # -----------------------------
6560# 정렬 기준
66- # -------------------------------
61+ # -----------------------------
6762def platform_rank (platform ):
6863 return 0 if platform == "백준" else 1
6964
@@ -75,9 +70,9 @@ def level_rank(platform, level):
7570 return int (m .group (1 )) if m else 999
7671 return 999
7772
78- # -------------------------------
79- # README 업데이트
80- # -------------------------------
73+ # -----------------------------
74+ # 디렉토리 처리
75+ # -----------------------------
8176def process_directory (dir_path ):
8277 readme_path = os .path .join (dir_path , "README.md" )
8378 if not os .path .isfile (readme_path ):
@@ -105,27 +100,45 @@ def process_directory(dir_path):
105100 status = "❌" if name .endswith ("_FAIL" ) else "✅"
106101 problems [f"pg_{ num } " ] = ("프로그래머스" , num , status )
107102
103+ # 기존 README 읽기
104+ existing_numbers = set ()
105+ with open (readme_path , "r" , encoding = "utf-8" ) as f :
106+ lines = f .readlines ()
107+ for line in lines :
108+ m = re .search (r"\|\s*(?:⬜|✅|❌)\s*\|\s*(백준|프로그래머스)\s*\|\s*(\d+)" , line )
109+ if m :
110+ existing_numbers .add (m .group (2 ))
111+
112+ # 기존 문제 + 새 문제 모두 rows로 준비
108113 rows = []
109114
110115 for _ , (platform , num , status ) in problems .items ():
111- info = fetch_boj_info (num ) if platform == "백준" else fetch_programmers_info (num )
112- if not info :
113- continue
116+ if platform == "백준" :
117+ info = fetch_boj_info (num )
118+ else :
119+ info = fetch_programmers_info (num )
120+
121+ title = info ["title" ] if info else "UNKNOWN"
122+ level = info ["level" ] if info else "UNKNOWN"
123+ link = info ["link" ] if info else "#"
124+
114125 rows .append ({
115126 "platform" : platform ,
116127 "number" : num ,
117128 "status" : status ,
118- "title" : info [ " title" ] ,
119- "level" : info [ " level" ] ,
120- "link" : info [ " link" ]
129+ "title" : title ,
130+ "level" : level ,
131+ "link" : link
121132 })
122133
134+ # 정렬
123135 rows .sort (key = lambda r : (
124136 platform_rank (r ["platform" ]),
125137 level_rank (r ["platform" ], r ["level" ]),
126138 int (r ["number" ])
127139 ))
128140
141+ # README 작성
129142 new_lines = [
130143 "| 순번 | 풀이 여부 | 플랫폼 | 문제 번호 | 문제 이름 | 난이도 | 풀이 링크 |\n " ,
131144 "|----|----|----|----|----|----|----|\n "
@@ -143,14 +156,20 @@ def process_directory(dir_path):
143156
144157 return True
145158
146- # -------------------------------
159+ # -----------------------------
147160# main
148- # -------------------------------
161+ # -----------------------------
149162def main ():
150163 for root , dirs , files in os .walk (ROOT_DIR ):
151164 if root .startswith ("./.git" ) or root .startswith ("./.github" ):
152165 continue
166+
167+ # ✅ 루트 README 제외
168+ if root == "." :
169+ continue
170+
153171 process_directory (root )
154172
173+
155174if __name__ == "__main__" :
156175 main ()
0 commit comments