[備忘録]ChatGPTの回答文をNotionへ一括登録

言語学習をしていて、とある英語系Youtuberの勧めていたNotionでの単語帳作成および学習進捗管理に感銘を受けたため、真似をし始めたのだが、学習自体よりNotionの編集に時間を取られ本末転倒になってしまったため、そちらの効率化を図ることにした。

## 今回やりたいNotionの編集作業
下記のような単語帳フォーマットを作成。今まではChatGPTの回答をコピペしては手でペーストしていたのだが、その作業だけで10フレーズで20分くらいかかってしまっていたので、回答を一回で該当項目にコピーしたい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3800965/e3581d62-1965-6da8-9c9c-f03f16e381cc.png)

本当は
①フレーズ入力
②ChatGPTに質問
③回答をコピーしてNotionへ登録
までを自動化したいのだが、openaiのAPIは従量課金制とのことなので、ChatGPTの部分はいったん手作業のままにしておく。（openaiのAPIも使ってみたいので、別の機会に行う）

## Notionインテグレーションを作成する
今回はPythonスクリプトでやってみたいのでこちらを参考にした。
参考:https://qiita.com/Yusuke_Pipipi/items/b44cb8442932019c52c9

まずはNotionインテグレーションを作成する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3800965/c7c7486c-c2fd-bc4b-eda0-88d86244c6b8.png)

権限設定はこんな感じ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3800965/8e847ecd-c1f0-9c78-62d4-9d1f6b817a71.png)

今回は単語帳に新規追加するだけなのでコンテンツを挿入以外は不要だが、初めて触るので、色々遊びたくてほぼフル権限。

## Pythonスクリプト作成
今回の処理は大まかには下記。
1\. Notionクライアント作成
2\. 文字入力の受付(ここでChatGPTの回答ペースト)
3\. 入力文字列の分割
4\. 分割した文字列を新規追加用Jsonに組み込む
5\. client.pages.createで新規単語帳ページ作成
※2～5はループ

### main処理
ループ前にクライアント作成し、ループ内で文字入力受付。入力文字列を辞書に切り分けて、それをJsonに挿入しcreateする流れ。
```python:add_vocaburary.py
import os
import re
from notion_client import Client
# main処理
# NotionのページURL
url = "https://www.notion.so/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx?v=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Notionのページにアクセスするためのクライアントを作成
token = os.environ.get("NOTION_TOKEN")
client = Client(auth=token)
database_id = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
try:
    while True:
        # ユーザーにテキストの入力を求める
        print("Please enter the text (type 'END' on a new line to finish):")
        user_text = []
        while True:
            line = input()
            if line == "END":
                break
            user_text.append(line)
        user_text = "\n".join(user_text)

        # 入力されたテキストを処理
        section_dict = extract_sections_from_text(user_text)

        # テキストを新規ページとして追加
        add_row_to_notion_database(database_id, section_dict)
        print("辞書登録スクリプト...中止したい場合はCTRL+C\n")
except KeyboardInterrupt:
    print("辞書登録スクリプトを終了しました")
```

### 辞書切り分け処理
今回使用したChatGPTのプロンプト。ところどころ英語が怪しいのはご愛敬。
```text:ChatGPTのプロンプト
Tell me the following about Swedish "Jag är förvånad"
How often do native speakers use it in conversation?
Answer like the following
Phrase:
[newline answer,  write the phrase which I asked]
Frequency of use:
[newline answer, choose one of the following four to express the frequency on a scale of 1-10  (heads-up being 8){not very often (frequency 1-2), moderately (frequency 3-5), often (frequency 6-8), very often (frequency 9-10)}].
Meaning:
[newline answer (just give the meaning briefly: (e.g.) hisnande would be breathtaking, amazing, wonderful)]
Etymology:
[newline answer]
collocation (this vocabulary + commonly used vocabulary: (e.g.) if you want to teach adrenalindriven, use adrenalindriven (this vocabulary) + äventyr (commonly used vocabulary) or adrenalindrivna (this vocabulary) + sport (often used together)] ■ source: [new line in answer] ■ collocation: [new line in answer vocabulary) + sport (vocabulary often used together), etc.)
[new line in answer].
Example sentences:
Example sentences: [insert a new line in the sentence (Answer the sentence that native speakers are most likely to use in a conversation, without “”.]
Other important points (points for use in conversation, similar expressions, answer both)
[newline answer]
```

下記がChatGPTの回答。例えば「Phrase:」の後が追加するページタイトルにあたる。
```text:ChatGPTの回答
Phrase:
Jag är förvånad

Frequency of use:
moderately (frequency 3-5)

Meaning:
I am surprised

Etymology:
The phrase "Jag är förvånad" consists of "jag" (I), "är" (am), and "förvånad" (surprised). The word "förvånad" comes from the Old Swedish "förvåna" meaning "to surprise," which itself derives from "för" (for) and "våna" (to wonder).

Collocation:
Jag är förvånad (this vocabulary) + över (commonly used vocabulary)

■ source: Common usage and linguistic patterns in Swedish conversation
■ collocation: Jag är förvånad (this vocabulary) + över (commonly used vocabulary)

Example sentences:
Jag är förvånad över resultatet.
Jag är förvånad att du kom så tidigt.

Other important points:
- In conversation, this phrase is often used to express mild to moderate surprise.
- Similar expressions include "Jag är chockad" (I am shocked) for a stronger reaction, or "Jag är imponerad" (I am impressed) when the surprise is positive.
```

末尾がコロンで終わる行で辞書のkeyを登録し、次のkeyになる行が来るまで文字列を値に追加していく。
```python:add_vocaburary.py
def extract_sections_from_text(user_input):
    # セクションタイトルに基づいて分割し、辞書に格納する
    section_dict = {}
    current_section_title = None

    for line in user_input.strip().split('\n'):
        if line.endswith(':'):
            current_section_title = line[:-1]
            section_dict[current_section_title] = ""
        elif current_section_title:
            section_dict[current_section_title] += line + '\n'
    # セクションから不要な改行をトリムする
    trimmed_section_dict = {key: value.strip() for key, value in section_dict.items()}
    return trimmed_section_dict
```



### createで新規ページ作成
Json書き馴れてない＋NotionAPI初のため、かなり苦戦。公式のリファレンスを見ながら作成。該当プロパティと子要素に辞書の文字列を挿入する。
参考:https://developers.notion.com/reference/page
```python:add_vocaburary.py
def add_row_to_notion_database(database_id, section_dict):
    response = client.pages.create(
        **{
            "parent": { "database_id": database_id
            },
            # ページのプロパティを指定する
            "properties": {
                "Vocabulary": {
                    "title": [
                        {
                            "text": {
                                "content": section_dict.get("Phrase", "")
                            }
                        }
                    ]},
                "frequency": {
                    "select": {
                        "name": section_dict.get("Frequency of use", "")
                    }
                }
            },  # end properties
            "children" : [{
                # toc block
                "object": "block",
                "type": "callout",
                "callout": {
                    'color': 'gray_background',
                    "rich_text": [{
                        "type": "text",
                        "text": {
                            "content": ""
                        }
                    }],
                'icon': {'emoji': '🗣', 'type': 'emoji'},
                }
            },{
                # toc block
                "object": "block",
                "type": "toggle",
                "toggle": {
                    'color': 'gray_background',
                    "rich_text": [{
                        'annotations': {'bold': True,
                                            'code': False,
                                            'color': 'blue',
                                            'italic': False,
                                            'strikethrough': False,
                                            'underline': False},
                        "type": "text",
                        "text": {
                            "content": "意味"
                        }
                    }],
                    "children":[{
                        "type" : "paragraph",
                        "paragraph": {
                            "rich_text": [{
                                "type": "text",
                                "text": {
                                    "content": section_dict.get("Meaning", "Meaning section not found.")
                                 }
                            }]
                        }
                    }]
                }
            },{
                # toc block
                "object": "block",
                "type": "toggle",
                "toggle": {
                    'color': 'gray_background',
                    "rich_text": [{
                        'annotations': {'bold': True,
                                            'code': False,
                                            'color': 'blue',
                                            'italic': False,
                                            'strikethrough': False,
                                            'underline': False},
                        "type": "text",
                        "text": {
                            "content": "語源"
                        }
                    }],
                    "children":[{
                        "type" : "paragraph",
                        "paragraph": {
                            "rich_text": [{
                                "type": "text",
                                "text": {
                                    "content": section_dict.get("Etymology", "Etymology section not found.")
                                 }
                            }]
                        }
                    }]
                }
            },{
                # toc block
                "object": "block",
                "type": "callout",
                "callout": {
                    'color': 'blue_background',
                    "rich_text": [{
                        'annotations': {'bold': True,
                                            'code': False,
                                            'color': 'blue',
                                            'italic': False,
                                            'strikethrough': False,
                                            'underline': False},
                        "type": "text",
                        "text": {
                            "content": "コロケーション"
                        }
                    }],
                    "children":[{
                        "type" : "paragraph",
                        "paragraph": {
                            "rich_text": [{
                                "type": "text",
                                "text": {
                                    "content": section_dict.get("Collocation", "Collocation section not found.")
                                 }
                            }]
                        }
                    }],
                'icon': {'emoji': '📎', 'type': 'emoji'}
                }
            },{
                # toc block
                "object": "block",
                "type": "callout",
                "callout": {
                    'color': 'blue_background',
                    "rich_text": [{
                        'annotations': {'bold': True,
                                            'code': False,
                                            'color': 'blue',
                                            'italic': False,
                                            'strikethrough': False,
                                            'underline': False},
                        "type": "text",
                        "text": {
                            "content": "例文"
                        }
                    }],
                    "children":[{
                        "type" : "paragraph",
                        "paragraph": {
                            "rich_text": [{
                                "type": "text",
                                "text": {
                                    "content": section_dict.get("Example sentences", "Example sentences section not found.")
                                 }
                            }]
                        }
                    }],
                'icon': {'emoji': '📎', 'type': 'emoji'}
                }
            },{
                # toc block
                "object": "block",
                "type": "callout",
                "callout": {
                    'color': 'yellow_background',
                    "rich_text": [{
                        'annotations': {'bold': True,
                                            'code': False,
                                            'color': 'default',
                                            'italic': False,
                                            'strikethrough': False,
                                            'underline': False},
                        "type": "text",
                        "text": {
                            "content": "イメージ"
                        }
                    }],
                'icon': {'emoji': '🖼️', 'type': 'emoji'},
                }
            },{
                # toc block
                "object": "block",
                "type": "callout",
                "callout": {
                    'color': 'yellow_background',
                    "rich_text": [{
                        'annotations': {'bold': True,
                                            'code': False,
                                            'color': 'default',
                                            'italic': False,
                                            'strikethrough': False,
                                            'underline': False},
                        "type": "text",
                        "text": {
                            "content": "自由記述"
                        }
                    }],
                    "children":[{
                        "type" : "paragraph",
                        "paragraph": {
                            "rich_text": [{
                                "type": "text",
                                "text": {
                                    "content": section_dict.get("Other important points", "Other important points section not found.")
                                 }
                            }]
                        }
                    }],
                'icon': {'emoji': '✏️', 'type': 'emoji'},
                }
            },]
          }
    )

    print("notion database create completed")
    print(response) # 追加した内容を表示する
```

実行してみる。
何とか形になっている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3800965/7ea1e399-934c-06c8-4d7e-41bb3b2dc338.png)

## 感想
実はPythonは仕事で少し触ったことがあるぐらいで、かなり知識が薄いのだが、ChatGPTに質問してひな形を作ってもらって、それを必要な修正を少し加えて完成させた。基礎知識があれば言語について知らなくても、簡単なものはChatGPTがなんとかしてしまう。恐ろしい時代になったものだ。コードを書く人間なんて、もう必要ないのかもしれない。