PythonアプリでAPIを使ったオフィススイート連携でドキュメント編集を可能にする方法

リモートワークやハイブリッドワークモデルの採用が増える中、コラボレーションアプリの開発への関心は依然として高いですね。COVID19のパンデミック規制のほとんどが解除されたにもかかわらず、企業やチームは柔軟性のメリットをすべて体験することができ、新しいニッチなソフトウェアに対する需要を刺激することができました。

データを扱うのに最も人気のある言語の1つは間違いなくPythonです。ファイルストレージと共有の上にコラボレーション機能を提供するアプリを構築してみてはいかがでしょうか。

この記事では、APIを使ってPython上の文書管理システムに[ONLYOFFICEドキュメントエディタ](https://www.onlyoffice.com/ja/developer-edition.aspx?utm_source=geektogeek&utm_medium=cpc&utm_campaign=Article_2_Python)の機能を追加する方法をご紹介します。思ったより簡単にできそうですね。

## PythonでのDMSについて

このパートでは、Pythonアプリを書き、ONLYOFFICEとの統合をその例でご紹介します。エディタを統合しようと思っているアプリは、閲覧/編集のために開く必要のあるオフィスファイルを扱う可能性が高いです。実際の設定でケースを説明するために、DMSの機能に似たアプリを作成することにします。このアプリでは、ファイルのダウンロードもできるようにする必要があります。

このアプリには、[Bottle](https://bottlepy.org/docs/dev/)フレームワークを使用します。`pip install bottle` コマンドで作業ディレクトリにインストールできます。次に、`main.py`（アプリのコード）と`index.tpl`（テンプレート）を作成し、`main.py` ファイルに次のコードを追加します。

```python
from bottle import route, run, template, get, static_file # connecting the framework and the necessary components
@route('/') # setting up routing for requests for /
def index():
    return template('index.tpl')  # showing template in response to request


run(host='localhost', port=8080)  # running the application on port 8080
```
アプリを起動すると、`http: // localhost:8080` に空のページが表示されます。ここで、開くためのファイルを追加して、テンプレートにその名前のリストを作成する必要があります。次に、`files`フォルダを作成し、そこに3つのファイル（docx, xlsx, pptx）を置きます。

そのファイル名を読み取るために、`listdir`コンポーネントを使用します。

```python
from os import listdir
```

では、`files`フォルダーにあるすべてのファイル名の変数を作ってみましょう。

```python
sample_files = [f for f in listdir('files')]
```

この変数をテンプレートで使用するには、`template` メソッドに渡す必要があります。

```python
def index():
    return template('index.tpl', sample_files=sample_files)
```

この変数をテンプレートに表示してみましょう：html

```html
%for file in sample_files:
    <div>
        <span>{{file}}</span>
    </div>
% end
```
アプリを再起動すると、ページ上にファイル名のリストが表示されます。さて、これらのファイルをすべてのアプリ利用者が利用できるようにする必要があります。

ここでは、そのための新しい方法をご紹介します。

```python
@get("/files/<filepath:re:.*\.*>")
def show_sample_files(filepath):
    return static_file(filepath, root="files")
```

## Pythonアプリでドキュメントを表示する

ONLYOFFICEエディタを使ってDocument Serverをインストールします。インストール方法はたくさんありますが、Dockerを使うことをお勧めします。

```
docker run -itd -p 80:80 onlyoffice/documentserver
```
テンプレート内のドキュメントエディターAPIを接続します。

```html
<script type="text/javascript" src="editor_url/web-apps/apps/api/documents/api.js"></script>
```
`editor_url` はドキュメントエディタへのリンクです。

各ファイルを開いて閲覧するためのボタンは：

```html
<button onclick="view('files/{{file}}')">view</button>
```
ここで、`id`を持つdivを追加する必要があります。

```html
<div id="editor"></div>
```
このdivの中でドキュメントエディタが開かれます。しかし、エディタを開く関数を呼び出した後でなければなりません。
```html
<script>
function view(filename) {
    if (/docx$/.exec(filename)) {
        filetype = 'word'
    }
    if (/xlsx$/.exec(filename)) {
        filetype = 'cell'
    }
    if (/pptx$/.exec(filename)) {
        filetype = 'slide'
    }

    new DocsAPI.DocEditor('editor',
        {
            documentType: filetype,
            document: {
                url: "host_url" + '/' + filename,
                title: filename
            },
            editorConfig: {mode: 'view'}
        });
  }
</script>
```
DocEditor関数の引数は2つです。エディタを開く要素の `id` と、エディタの設定を指定するjsonです。

すべてのパラメータは、[公式のAPIドキュメント](https://api.onlyoffice.com/editors/config/)に記載されています。この例では、必須パラメータである `documentType`, `document.url` と `editorConfig.mode` を使用します。また、 `title` も追加してみよう。これはエディタに表示されるファイル名です。

ドキュメントの種類 (`documentType`) は、そのフォーマットによって識別されます (docxはword、xlsxはcell、pptxはslide)。

`document.url`に注目してください。これはこれから開こうとしているファイルへのリンクです。

これで、Pythonアプリでドキュメントを表示するためのすべてが揃いました。

## ファイルの編集

「編集」ボタンを追加してみましょう。

```html
<button onclick="edit('files/{{file}}')">edit</button>
```
次に、ファイルを開いて編集するための新しい関数を作成する必要があります。これは「表示」関数に似ているので、共通部分を別の関数にしましょう。

これで3つの関数ができました。

```html
<script>
    var editor;
    function view(filename) {
        if (editor) {
            editor.destroyEditor()
        }
        editor = new DocsAPI.DocEditor('editor',
            {
                documentType: get_file_type(filename),
                document: {
                    url: "host_url" + '/' + filename,
                    title: filename
                },
                editorConfig: {mode: 'view'}
            });
    }

    function edit(filename) {
        if (editor) {
            editor.destroyEditor()
        }
        editor = new DocsAPI.DocEditor('editor',
            {
                documentType: get_file_type(filename),
                document: {
                    url: "host_url" + '/' + filename,
                    title: filename
                }
            });
    }

    function get_file_type(filename) {

        if (/docx$/.exec(filename)) {
            return 'word'
        }
        if (/xlsx$/.exec(filename)) {
            return 'cell'
        }
        if (/pptx$/.exec(filename)) {
            return 'slide'
        }
    }
</script>
```
`destroyEditor` はエディタを開いていた場合、そのエディタを閉じます。

デフォルトでは、 `editorConfig` パラメータの値は `{"mode": "edit"}` という値を持っています。そのため、 `edit()` 関数にはこのパラメータがありません。

これで、ファイルが編集用に開かれるようになります。

## 文書の共同編集

共同編集は、エディタの設定で同じドキュメントに対して同じ [document.key](https://api.onlyoffice.com/editors/config/document#key) を使用することで実装されています。このキーがない場合、エディタはファイルを開くたびに編集セッションを作成します。

共同編集のために同じ編集セッションに接続させるためには、それぞれのdocにユニークなキーを設定する必要があります。ここでは、`filename + "_key"` という形式のキーを使用することにしましょう。これを `document` が存在するすべての設定に追加する必要がある。

json

```
document: {
    url: "host_url" + '/' + filepath,
    title: filename,
    key: filename + '_key'
},
```
## ファイルの保存

ONLYOFFICEは通常、作業中にドキュメントに加えたすべての変更点を保存します。エディタを閉じた後、Document Serverは保存するファイルのバージョンをビルドして、callbackUrlアドレスにリクエストを送信します。このリクエストには `document.key` とビルドされたばかりのファイルへのリンクが含まれています。

実運用では、`document.key` を使用して古いバージョンのファイルを見つけ、新しいものに置き換えます。今回のケースでは、データベースがないので、callbackUrlを使用してファイル名を送信します。

editorConfig.callbackUrlの設定で、`callbackUrl`を指定します。このパラメータを追加すると、`edit()` メソッドは次のようになります。

```js
    function edit(filename) {
        const filepath = 'files/' + filename;
        if (editor) {
            editor.destroyEditor()
        }
        editor = new DocsAPI.DocEditor('editor',
            {
                documentType: get_file_type(filepath),
                document: {
                    url: "host_url" + '/' + filepath,
                    title: filename, 
                    key: filename + '_key'
                }
                ,
                editorConfig: {
                    mode: 'edit',
                    callbackUrl: "host_url" + '/callback?filename=' + filename  // add file name as a request parameter
                }
            });
    }
```
次に、POST リクエストを `/callback` アドレスに取得した後にファイルを保存するメソッドを記述する必要があります。


```python
@post("/callback") # processing post requests for /callback
def callback():
    if request.json['status'] == 2: 
        file = requests.get(request.json['url']).content
        with open('files/' + request.query['filename'], 'wb') as f:
            f.write(file)
    return "{\"error\":0}"
```

これで、エディタを閉じた後、新しいバージョンのファイルがストレージに保存されるようになりました。

## ユーザーの管理

アプリ内にユーザーがいる場合、エディタ設定にユーザーの識別子（idとname）を記述してください。こうすることで、誰がドキュメントを編集しているのかを確認することができます。

例として、インターフェイスでユーザーを選択する機能を追加してみましょう。

```html
<select id="user_selector" onchange="pick_user()">
    <option value="1" selected="selected">JD</option>
    <option value="2">Turk</option>
    <option value="3">Elliot</option>
    <option value="4">Carla</option>
</select>
```
`pick_user()` という関数の呼び出しを `<script>` というタグの最初に追加しましょう。この関数の中で、idとユーザー名を表す変数を初期化します。

```js
        function pick_user() {
        const user_selector = document.getElementById("user_selector");
        this.current_user_name = user_selector.options[user_selector.selectedIndex].text;
        this.current_user_id = user_selector.options[user_selector.selectedIndex].value;
    }
```

次に、エディタ設定に `editorConfig.user.id` と `editorConfig.user.name` を使って、[ユーザーの設定](https://api.onlyoffice.com/editors/config/editor#user)を追加します。これらのパラメータを、ファイル編集機能のエディタ設定に追加してみましょう。

```js
    function edit(filename) {
        const filepath = 'files/' + filename;
        if (editor) {
            editor.destroyEditor()
        }
        editor = new DocsAPI.DocEditor('editor',
            {
                documentType: get_file_type(filepath),
                document: {
                    url: "host_url" + '/' + filepath,
                    title: filename
                },
                editorConfig: {
                    mode: 'edit',
                    callbackUrl: "host_url" + '/callback?filename=' + filename,
                    user: {
                        id: this.current_user_id,
                        name: this.current_user_name
                    }
                }
            });
    }
```
このシンプルな例が、あなたのPythonアプリに[ONLYOFFICE](https://www.onlyoffice.com/ja/developer-edition.aspx?utm_source=dzone&utm_medium=cpc&utm_campaign=Article_2_Python)を統合する助けになることを願っています。

もしあなたがPython開発者であれば、お考えやご経験をここで共有していただければ、Pythonベースの環境へのONLYOFFICE機能の統合について、さらに詳しく説明することができます。