ほぼ老人のプログラミング日記

定年後の平凡なサラリーマンの趣味の日記

Knowledge→GROWI 移行 (7)

移行実施

いよいよ、knowledge から GROWI にデータを移行します。移行に際して、下記のような移行用のプログラムを作成しました。使い捨てです。

import psycopg2
from psycopg2.extras import DictCursor

from growiclient import GrowiClient

# DB接続情報
DB_HOST = 'postgres'
DB_PORT = '5432'
DB_NAME = 'knowledgedb'
DB_USER = 'kbadmin'
DB_PASS = '**********'

# GROWI 接続情報
GROWI_HOST = 'growi'
GROWI_PORT = '3000'
GROWI_SSL = False
GROWI_API_KEY = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
GROWI_USER = 'tiger'


# DB接続関数
def get_connection():
    return psycopg2.connect('postgresql://{user}:{password}@{host}:{port}/{dbname}'
                            .format(user=DB_USER,
                                    password=DB_PASS,
                                    host=DB_HOST,
                                    port=DB_PORT,
                                    dbname=DB_NAME))


# 添付ファイルの移行
def migrate_attachments(conn, growi_client, page, id):
    with conn.cursor(name='files_cursor', cursor_factory=DictCursor) as file_cur:
        id_col = 'knowledge_id'
        if growi_client.is_draft():
            id_col = 'draft_id'
        file_cur.execute('select file_no, knowledge_id, comment_no, draft_id, file_name, file_binary '
                         'from knowledge_files '
                         'where {} = %s'.format(id_col), (id, ))
        for file_row in file_cur:
            print(file_row['file_name'])
            file_path = './knowledge_to_growi/attachments/' + file_row['file_name']
            with open(file_path, 'wb') as f:
                f.write(file_row['file_binary'])
            file_url = growi_client.set_attachment(page, file_path)
            page.replace_attachment(file_row['file_name'], file_url)


# Knowledge -> GROWI ページ移行
def migrage_knowledge(conn, growi_client):
    with conn.cursor(name='knowledges_cursor', cursor_factory=DictCursor) as kb_cur:
        if growi_client.is_draft():
            kb_cur.execute('select draft_id, tag_names, title, content from draft_knowledges '
                           'where delete_flag = 0 '
                           '  and (knowledge_id is null or knowledge_id not in (select knowledge_id from knowledges)) '
                           'order by draft_id')
        else:
            kb_cur.execute('select knowledge_id, tag_names, title, content '
                           'from knowledges '
                           'where delete_flag = 0 '
                           'order by knowledge_id')
        titles = {}
        for kb_row in kb_cur:
            if growi_client.is_draft():
                id = kb_row['draft_id']
            else:
                id = kb_row['knowledge_id']
            print(str(id) + ' : ' + kb_row['title'])
            page_tags = []
            if kb_row['tag_names']:
                page_tags = kb_row['tag_names'].replace(chr(0xa0), '').split(',')
                if len(page_tags) == 1:
                    page_tags.append('')
            title = kb_row['title']
            if title in titles:
                titles[title] += 1
                title += '({})'.format(str(titles[title]))
            else:
                titles[title] = 1
            content = kb_row['content']
            if len(content) == 0:
                content = "## {}".format(title)
            page = growi_client.create_page(title, page_tags, content)
            migrate_attachments(conn, growi_client, page, id)
            growi_client.update_page(page)


with get_connection() as conn:
    # 並び順を後ろの方にするためにドラフトページを先に移行
    growi_client = GrowiClient(
        GROWI_HOST, GROWI_PORT, GROWI_API_KEY, GROWI_USER, GROWI_SSL, True)
    migrage_knowledge(conn, growi_client)

    # 公開ページの移行
    growi_client = GrowiClient(
        GROWI_HOST, GROWI_PORT, GROWI_API_KEY, GROWI_USER, GROWI_SSL)
    migrage_knowledge(conn, growi_client)

実行して、簡単に内容を確認してみましたが大丈夫そうです。
移行元のデータベースは Docker コンテナの状態で保管しておくので、問題が見つかれば、その時に対応することにします。


実はもっと簡単に済むんじゃないかと思っていたのですが、意外に時間がかかりました。しかし、Python から使える GROWI のクライアントを手に入れたので、今後何かに使えるんじゃないかと思っています。