Pyinstallerを用いて組合せ最適化(Pulp)でコンテナ納品計画を作成するexeファイルを作ってみた

# はじめに
- 前回の記事[「組合せ最適化でコンテナ納品計画を作成してみた」](https://qiita.com/mmura001/items/a826fcc89da8ae575cac)をPython環境を持たない倉庫担当者でも気軽に使えるように、Pyinstallerでexeファイル化しました。
- exeファイル作成時に多少詰まった部分もあったので、備忘録も踏まえて記します。
- これからpulpを用いたコードをPyinstallerでexeファイル化する方に少しでも貢献できればと思います。

# 背景
- 前回、コンテナ納品計画を最適化するコードを作った。
- しかし、倉庫の各担当者のPCにPythonの環境を構築した後、このコードを渡して使用してもらうのはハードルが高い。
- 幸い倉庫ではWindowsを使用しているので、作ったコードをexeファイル化して配布することを考えた。

# 要旨
- 倉庫担当者がWindows環境(python非搭載)で簡単に使えるよう、[前回書いたpythonコード](https://qiita.com/mmura001/items/a826fcc89da8ae575cac)をPyinstallerを用いてexeファイル化した。

# 実行環境
以下の環境で実施しました。

- Python: 3.7.9
- PuLP: 2.6.0
- ortoolpy: 0.2.38
- numpy: 1.21.5
- pandas: 1.3.5
- pyinstaller: 4.8


# Pyinstallerを用いてexeファイルを作成
### ■ Pyinstallerの導入

```terminal:terminal
pip install pyinstaller
```

### ■ exeファイルの作成
- 倉庫の担当者が使用することを考えて--onefileと--noconsoleでexeファイルを作成。

```terminal:terminal
pyinstaller devan_opt.py --onefile --noconsole
```
distフォルダ内に **devan_opt.exe** が作成される。

### ■ ファイル構成の変更
- 元のdevan_opt.pyは**devan_booking.csv**を**inputフォルダ**から取得して最適化計算し、 **devan_opt.csv**を**outputファイル** へ返す。
- 上記仕様に沿うように **inputフォルダ** と **outputフォルダ** を **distフォルダ** 内に追加する。
- また、**devan_booking.csv**を**inputフォルダ**へ格納する。

**<変更前>**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985685/cce60d18-aab4-aca2-0024-250bfab20460.png)

**<変更後(distフォルダへinput・outputフォルダを格納)>**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985685/5557e32e-b536-38c0-24dd-fdc9b4851fd3.png)

**<変更後(inputフォルダへcsvを格納)>**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985685/816ab642-9fb6-5de6-6a94-d9df6bbb8648.png)



### ■ devan_opt.exeの実行（エラー発生！）
- devan_opt.exeをクリックしてテストしたが、devan_opt.csvは出力されなかった。
- エラー内容が出力されるようにしてterminalから再度実行すると、以下のエラーメッセージが出力された。

```terminal:terminal
  File "devan_opt.py". line 88 in <module>
  File "pulp\pulp.py". line 1913 in solve
AttributeError: 'NoneType' object has no attribute 'actualSolve'
[10116] Failed to execute script 'devan_opt' due to unhandled exception!

```

### ■ エラーの対処法
- 調べてみるとソルバ部分のエラーで、以下2つで解消されるとのこと。
 1. devan_opt.pyでソルバのパスを指定
 2. ソルバ(今回はcbc.exe)を自作exeファイルと同じフォルダに格納

### ■ devan_opt.pyの修正・exeファイルを再作成
- [前回記事](https://qiita.com/mmura001/items/a826fcc89da8ae575cac)の以下2か所を次のように修正。

```python:python(修正前1)
import numpy as np
import pandas as pd
from datetime import datetime as dt
from datetime import timedelta as td
from pulp import*
from ortoolpy import addvars, addbinvars
```

```python:python(修正後1)
import os #追加
import numpy as np
import pandas as pd
from datetime import datetime as dt
from datetime import timedelta as td
from pulp import*
from ortoolpy import addvars, addbinvars
```

```python:python(修正前2)
m.solve()
```

```python:python(修正後2)
currentpath = os.getcwd() # カレントディレクトリのパスを取得
solverpath = os.path.join(currentpath,'cbc.exe') # カレントディレクトリ＋'cbc.exe'のパスを取得

m.solve(COIN_CMD(path=solverpath)) # 'cbc.exe'のパスを指定
```

- pyinstallerを使って再度exeファイルを作成。

```terminal:terminal
pyinstaller devan_opt.py --onefile --noconsole
```

### ■ cbc.exeの格納
- pulpにデフォルトで入っているcbc.exeをコピーした。
    - python環境のLib/site-packages/pulp/solverdir/cbc/win/64フォルダから"cbc.exe"のみ取り出す。
    - distフォルダ内へ格納。
        - input・outputファイルと同様にcbc.exeファイルを格納する。
- [ソルバ(cbc.exe)のダウンロード先](https://www.coin-or.org/download/binary/Cbc/)からダウンロードしてもよい。


**<変更後(distフォルダへcbc.exeを格納)>**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985685/2503f6bd-50b8-099e-fd57-1e8dc2beb269.png)

### ■ devan_opt.exeの再実行（成功）
- devan_opt.exeをダブルクリックしてテストした結果、outputフォルダへdevan_opt.csvが出力された。
- 結果も前回と同じものが得られた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/985685/ac98fb2c-0bbe-ddd3-f9d6-2be5514bb814.png)

### ■ 補足(現場への導入方法)
- 当初はこのdistファイル一式をzip形式で倉庫担当者へメール送信することを想定していました。*cbc.exeのみはダウンロードしてもらう。
- しかし、pyinstallerで作ったexeファイルの容量が大きく、添付容量の上限オーバーでメール送信できませんでした。
- そこで、自作のexeファイルと使い方.txtをZipファイルにしてGithub上に公開し、担当者へリンクのみメールで送る運用にします。
- 使い方.txtにはソルバ(cbc.exe)のダウンロード先やフォルダの構成などを記載します。


# 所感
今回の方法を業務手順書に落とし込むと以下のようになります。

- importフォルダに予約表csvを入れる。
- exeファイルをクリックする。
- outputフォルダから納品計画csvを取り出す。

上記内容であれば、現場の方にも違和感なく受け入れていただけるのではないかと個人的には感じました。
したがって、pulpを現場で使ってもらうためにpyinstallerを用いたexeファイル化は有効と思います。

いずれにせよ、数年後にはExcelやWordを使うのと同じくらいの感覚でpulpが現場に浸透していれば良いなと思う次第です。

# 参考
- [exeファイルpulpエラーについてのやり取り](https://groups.google.com/g/pulp-or-discuss/c/3bS3KIc9-QM?pli=1)
- [ソルバ(cbc.exe)のダウンロード先](https://www.coin-or.org/download/binary/Cbc/)
- [【PyInstaller】Pythonで作成したアプリをexe化して配布する](https://qiita.com/mochi_yu2/items/ce598ec57afe44453e98)