노션 꾸미기에 재미가 들려 백준 문제 푼 것들을 꾸며서 올리다가 너무 귀찮아서
문제를 풀면 자동!!으로 새 문제 Page를 생성해주는 프로그램을 만들었다
바로 사용법을 볼려면 2️⃣ 로 이동!
프로그램 Flow Chart
다음 함수들로 구현한다
def save_file_list(path)
def update_notion(file_list)
def post_page(problem_info, submit_code, client)
def get_problem(problem_id)
def extract_prob_info(data)
def code_comments(param)
1) save_file_list
def save_file_list(path): file_list = [] for root, dirs, files in os.walk(path): for file_name in files: file_list.append(root + '/' + file_name) return file_list
path 안에 있는 모든 코드 파일 (.py / .cpp / .js)의 경로를 리스트안에 저장하여 리턴한다.
2) update_notion
def update_notion(f_list): try: client = NotionClient(token_v2=notion_token_v2) except: print("Notion Client Error!") return page = client.get_block(notion_page_id) # get list of current pages in page current_pages = [] for child in page.children: if isinstance(child, PageBlock): cut = child.title.index(' ') current_pages.append(child.title[:cut]) for f_name in f_list: f_name = f_name[::-1] dot = f_name.index('.') slash = f_name.index('/') # get problem id problem_id = f_name[dot + 1:slash][::-1] if problem_id in current_pages: continue # get submit code code_file = f_name[:dot + 1][::-1] submit_code = langs[code_file] problem_info = extract_prob_info(get_problem(problem_id)) post_page(problem_info, submit_code, client)
노션 토큰을 이용해 NotionClient를 열고, 백준 Page를 가져와 현재 올라와있는 문제들을 current_pages에 저장한다
f_list에 있는 파일 중, current_pages에 존재하는 파일에 대해선 넘기고, 나머지 새로운 파일들을 post한다.
또한 코드 파일의 확장자명 (.py / .cpp)를 추출해 선언된 lang 딕셔너리로 언어명(submit_code)을 받아온다 (python, c++).
이는 노션에 코드를 올릴 때, Syntax Highlighting을 언어별로 실행시키게 하기 위해서이다.
3) post_page
def post_page(problem_info, submit_code, client): page = client.get_block(notion_page_id) # page title post_title = str(problem_info[0]) + ' - ' + problem_info[1] new_page = page.children.add_new(PageBlock, title=post_title) # problem link link_text_block = new_page.children.add_new(TextBlock) link_text_block.title = f'[문제 링크](https://www.acmicpc.net/problem/{problem_info[0]})' # page icon tier = str(problem_info[2]) icon_url = f'https://d2gd6pc034wcta.cloudfront.net/tier/{tier}.svg' new_page.icon = icon_url # page callout callout_info = '/'.join(problem_info[3]) callout = new_page.children.add_new(CalloutBlock) callout.title = callout_info callout.icon = "💡" callout.color = "gray_background" # read code submit_path = glob.glob(f'{code_path}/{problem_info[0]}.*')[0] with open(submit_path, "r", encoding='utf-8') as f: code_lines = f.readlines() code_lines = [' ' + line for line in code_lines] code = ''.join(code_lines) # code block new_code_block = new_page.children.add_new(CodeBlock) new_code_block.title = code new_code_block.language = submit_code # code comments new_text_block = new_page.children.add_new(TextBlock) new_text_block.title = code_comments("\n".join(code_lines))
1) page title : 문제 번호 - 문제
2) page icon : problem_info 에서 받아온 level로 티어 아이콘 온라인 링크 삽입
3) problem link : 문제 링크(링크 삽입)
4) page callout : 다음 사진과 같이 문제 Tag 추가
4) code block : 코드 파일을 읽어 와 추가한다
5) code comments : GPT-4로 코드 설명을 가져와 추가한다
4) get_problem / extract_prob_info
def get_problem(prob_n): url = "https://solved.ac/api/v3/problem/show" querystring = {"problemId": str(prob_n)} headers = {"Accept": "application/json"} try: response = requests.get(url, headers=headers, params=querystring) except: print("Solved.ac API ERROR!") return return response.json() def extract_prob_info(data): problemId = data['problemId'] titleKo = data['titleKo'] level = data['level'] tags = [tag['displayNames'][0]['name'] for tag in data['tags']] return [problemId, titleKo, level, tags]
save_file_list 에서 얻어온 파일 경로에서 문제 번호를 추출해 get_problem함수로 넘기면 문제 정보가 json파일로 온다.
json파일을 extraxt_prob_info로 넘겨 [문제 번호, 문제 타이틀, 레벨(티어), 태그] 만 저장한다. 이 problem_info 리스트는 위 함수들에서 쓰인다
5) code_comments
def code_comments(param): try: response = openai.ChatCompletion.create( model='gpt-4', messages=[ {'role': 'system', 'content': ''}, {'role': 'user', 'content': "다음 코드의 작동 원리를 간결하게 한글로 설명해라 (말투는 \"~이다\"): " + param} ], temperature=0.4 ) except: print("OpenAI API Error!") return return response['choices'][0]['message']['content']
프로그램 실행을 위해 아래 네 가지 key를 받아온다.
1) OpenAI API Key
*OpenAI API 요청은 유료이므로 돈까지 써가면서 본인 코드에 GPT의 설명을 업로드 하고 싶지 않다면 Line 94, 95을 주석 처리 하고 2번으로 넘어가면 된다. (이정도 API 요청은 가격이 몇십원 밖에 안하긴 하다)
2) Notion Token
1) 웹 페이지로 노션을 접속한다
2) 코드를 업로드할 Notion 상위 Page를 만든다
3) 페이지를 우클릭 한 후, 아래 "검사"를 선택한다
상단 메뉴에서 맨 우측 화살표를 클릭하고, Application을 선택해 들어간다.
좌측 Cookies 에서 www.notion.so를 선택하고, token_v2를 찾는다
바로 옆 박스를 클릭하면 아래에 박스에 본인의 token이 나온다
3) Notion Page ID
1) 코드를 Post할 노션 페이지로 가서 우측 상단 공유 버튼을 누른다
2) "공유" 와 "게시"이 있는데, 우선 "공유" 탭에서 다음과 같이 모든 사용자가 편집을 할 수 있도록 체크되있는지 확인한다.
만약 이렇게 [모든 사용자]를 체크하는 탭이 없다면, 워크스페이스를 새로 생성하면 아마 뜰 것이다. (여러 계정으로 해본 결과, 거의 다 되는데 안되는 계정도 있어서 정확한 해결 방법은 못 찾았다...)
3) "게시" 탭으로 가 웹에 게시 후, 웹 링크를 복사한다.
링크에서 아래 부분을 복사한다. (- 다음부터 ? 전까지)
이게 본인의 page id 이다.
4) 본인의 코드 디렉토리
백준을 푸는 코드를 보관할 디렉토리를 만든다
ex) /Users/user/Desktop/Code/Baekjoon
앞으로 백준을 풀 때, 이 디렉토리에 문제번호를 이름으로 파일을 저장하면 된다.
ex) /Users/user/Desktop/Code/Baekjoon/1016.py
0) 환경 Setup
터미널 실행
git clone으로 프로젝트 다운로드
git clone https://github.com/chaseungjoon/Baekjoon-Auto-Notion.git
cd Baekjoon-Auto-Notion
pip install -r requirements.txt
🪟Windows
notepad keys.py
🍎Mac
open keys.py
1) 백준 문제를 푼다
예시를 위해 1016번 문제를 풀었다.
2) 푼 문제 코드 파일을 생성하여 코드 디렉토리에 저장한다
3) 프로그램을 실행한다
cd Baekjoon-Auto-Notion
python main.py
프로그램을 실행하면, 자동으로 코드 디렉토리(Baekjoon 폴더)에서 새로 추가된 문제 번호를 찾아 Notion으로 커밋한다.
추천) 문제를 풀고 바로 터미널로 커밋을 하기 위해 사용자 지정 명령어를 만들면 좋다.
프로그램이 실행되면, Baekjoon 디렉토리에 있는 모든 파일이 Notion 페이지로 업로드된다.
만약 이미 같은 문제 번호로 Notion 페이지가 존재한다면 스킵하고 새로 추가된 문제만 페이지를 생성한다.
결과
만약 본인이 직접 추가하고 싶은 텍스트 블럭, 이미지, 아이콘이 있다면
Notion API Document에서 방법을 찾아 코드를 추가하면 된다.
모든 과정을 완벽히 진행했는데 Notion Client Error가 계속 뜬다면 Token 값이 변경됐을 가능성이 높다. (계속 로그인 되어 있다면 토큰 값은 변하지 않는다, 로그아웃하면 변경된다)
다시 토큰을 확인하여 변한 값으로 바꿔준다.
또한, urllib3의 버전이 높으면 이상하게 client를 가져오는 과정에서 에러가 난다. 이를 처리하기 위해 requirements에서 패키지를 다운할 때, 되는 버전인 1.26.15를 다운하게 적어놨다.
만약 에러가 뜬다면 다음 코드로 본인의 urllib3 버전을 확인하자
pip uninstall urllib3
만약 1.26.15가 아니면, 삭제하고 다음과 같이 재설치하자
pip install urllib3==1.26.15
와 좋은 프로그램이네용 .. 👍👍