백준 문제 풀고 Notion에 자동으로 커밋하기

차승준·2023년 6월 28일
79

Projects

목록 보기
3/6
post-thumbnail

노션 꾸미기에 재미가 들려 백준 문제 푼 것들을 꾸며서 올리다가 너무 귀찮아서
문제를 풀면 자동!!으로 새 문제 Page를 생성해주는 프로그램을 만들었다

바로 사용법을 볼려면 2️⃣ 로 이동!

1️⃣ 프로그램 구성

프로그램 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)
  • 이 프로그램의 body 격인 함수로 파일 리스트 중 노션에 post 되지 않은 파일에 대해
    extract_prob_info(), get_problem(), post_page() 함수로 노션에 새로운 page를 만든다.

노션 토큰을 이용해 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))
  • 파라미터로 받아온 Client를 이용해 백준 코드 페이지에 children page를 생성하고 다음 요소들을 추가한다.

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]
  • 아쉽게 백준은 공식 API도 없을 뿐더러 웹 스크레이핑도 금지하고 있다. 다행히 solvedac API로 문제 정보를 가져올 수 있다.

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']
  • 이 함수로 코드 문자열을 넘겨주면 적당히 GPT가 설명을 해준다. 그냥 코드만 올리면 허전할 것 같아 간단히 추가했다.

완성 코드

2️⃣ 사용법

1. 초기 설정

프로그램 실행을 위해 아래 네 가지 key를 받아온다.

1) OpenAI API Key

*OpenAI API 요청은 유료이므로 돈까지 써가면서 본인 코드에 GPT의 설명을 업로드 하고 싶지 않다면 Line 94, 95을 주석 처리 하고 2번으로 넘어가면 된다. (이정도 API 요청은 가격이 몇십원 밖에 안하긴 하다)

-> OpenAI API Key 발급

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

2. 프로그램 실행하기

0) 환경 Setup

  1. 터미널 실행

  2. git clone으로 프로젝트 다운로드

git clone https://github.com/chaseungjoon/Baekjoon-Auto-Notion.git
cd Baekjoon-Auto-Notion
  1. 필요 패키지 다운로드
pip install -r requirements.txt
  1. 위에서 얻은 key 값들 지정 (OpenAI API Key, Token, Page ID, Code Path)

🪟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에서 방법을 찾아 코드를 추가하면 된다.

3. 오류 핸들링

모든 과정을 완벽히 진행했는데 Notion Client Error가 계속 뜬다면 Token 값이 변경됐을 가능성이 높다. (계속 로그인 되어 있다면 토큰 값은 변하지 않는다, 로그아웃하면 변경된다)

다시 토큰을 확인하여 변한 값으로 바꿔준다.

또한, urllib3의 버전이 높으면 이상하게 client를 가져오는 과정에서 에러가 난다. 이를 처리하기 위해 requirements에서 패키지를 다운할 때, 되는 버전인 1.26.15를 다운하게 적어놨다.

만약 에러가 뜬다면 다음 코드로 본인의 urllib3 버전을 확인하자

pip uninstall urllib3

만약 1.26.15가 아니면, 삭제하고 다음과 같이 재설치하자

pip install urllib3==1.26.15
profile
cau cse

7개의 댓글

comment-user-thumbnail
2023년 7월 5일

와 좋은 프로그램이네용 .. 👍👍

1개의 답글
comment-user-thumbnail
2023년 7월 7일

저도 꼭 다음에 써봐야겠습니다!

1개의 답글
comment-user-thumbnail
2023년 7월 8일

잘 읽었습니다!
https://me2.kr/tiyHh
다양한 코딩연습문제 사이트 정리되어 있는 것 같아 공유드려요 ~

1개의 답글
comment-user-thumbnail
2024년 4월 13일

와 신기합니다 좋은 정보 알아가네요!! 이미 푼 문제도 올릴 수 있는지 알아봐야겠어요..!

답글 달기