今回は、PASE環境におけるPythonプログラムの実行に関する内容です。
サンプルプログラムは下記の通りです。
| No. | ファイル名 | 内容 |
|---|---|---|
| 1 | config.ini | CSV->Excel変換の初期設定ファイル |
| 2 | csvExcel_log_conf.json | CSV->Excelログ設定ファイル |
| 3 | mail_body.txt | CSV->Excelにて生成した.xlsxファイルをメールで送信するときの本文(body)のテンプレート |
| 4 | csvExcel.py | CSV->Excel変換のPythonスクリプト本体 |
| 5 | csvExcelConst.py | CSV->Excel変換のPythonスクリプト定数定義ファイル |
| 6 | csvExcelConvert.py | CSV->Excel変換のPythonスクリプトExcelコンバート用支援ファイル |
| 7 | csvExcelImportXml.py | CSV->Excel変換のPythonスクリプトXML処理ファイル |
| 8 | csvExcelUtil.py | CSV->Excel変換のPythonスクリプトユーティリティ |
上記のファイルは、/opt/excelディレクトリー下にftpでバイナリー転送します。
PythonでのCSV->Excel変換の大きな流れは、下記となります。
- CSVファイルをpandasでDataFrameに取込み、.xlsxファイルに書き込む
- openpyxlで書込んだxlsxファイルをワークブックに読み込み、シート名設定・フォントや列幅等を設定
- openpyxlでxlsxファイルに上書き出力
1. 初期設定ファイル
3つの設定ファイルがあります。以下は、PASE環境/opt/excelに配置してください。
| No. | 設定ファイル名 | 内容 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | config.ini |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 2 | mail_body.txt |
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 3 | csvExcel_log_conf.json | ログ設定ファイル(ログフォーマットやバックアップファイル数などの指定)JSONフォーマットで記述 詳細は、Pythonのloggerドキュメントを参照ください。 |
2.Pythonスクリプト
2.1 Pythonログ設定ファイル(csvExcel_log_conf.json)
PASE環境で実行するPythonのログ出力設定です。
JSONフォーマットで記述します。
この例では、ログをConsoleとfileに出力します。
2.2 定数定義(csvExcelConst.py)
CSV->Excel変換のPythonスクリプトで使用する定数を定義したものです。
定数を定義しておくと入力ミスをなくせます。(開発ツールのコード補完機能)
Pythonの変数は事前定義ではありません。新しい名称名として判断されます。一見便利なのですが、英大文字・小文字が混在している場合や、スペルをミスした場合、DEBUGが非常に大変です。(※値が入っているはずの変数の値が設定されていない)
下記は定数定義の抜粋です。このようにしておくと、def_graph[csvExcelConst.GRAPH_TYPE]と記述できるため、入力ミスをしても開発ツールがアシストしてくれます。
2.3 ユーティリティ関数(csvExcelUtil.py)
ソースファイルを巨大で長くしてしまうと、修正の必要がない箇所を勘違いで修正してしまったり、修正するべき箇所を探すのが大変になります。そこで、ソースファイルを機能単位に分けています。このユーティリティ関数クラスは処理支援関係になります。詳しくはソースファイルを確認してください。
csvExcelUtil.pyでは、電子メールを送信するためのデータ構造体クラスも定義(宣言)しています。下記にその一部コードを記載します。
ユーティリティ・クラスのメソッド一覧
| No. | メソッド | 内容 | 備考 |
|---|---|---|---|
| 1 | get_parse_file_name | フルパスファイル名(フォルダー、ファイル名、拡張子)を取得する | |
| 2 | load_csv_file | “CSVファイルを読込みDataFrameを取得する エンコードはCp932(マイクロソフトのシフトJIS)を指定(コード変換なし) |
Pandasを使用。コード変換エラーを無視するオプションignoreを指定※外字を通過させるため |
|
|||
| 3 | create_empty_excel_file | 指定されたファイル名に空のブックを作成し書き込む | Openpyxlを使用 |
| 4 | load_excel_file | 指定されたファイル名からExcelブックとして読み込む。 | 同上 |
| 5 | get_sheet_names | 指定されたワークブックのシート名一覧とシート数を取得する | 同上 |
| 6 | get_active_sheet | 指定されたワークブックのアクティブシートを取得する | 同上 |
| 7 | change_active_sheet | 指定されたワークブックのアクティブシートを変更する | 同上 |
| 8 | rename_sheet_name | 指定されたシート名を変更する | 同上 |
| 9 | check_sheet_names | 指定されたシート名がワークブックに存在するかチェックする | 同上 |
| 10 | add_sheet_name | ワークブックの最後にシートを追加する | 同上 |
| 11 | delete_sheet_name | 指定したシートをワークブックから削除する | 同上 |
| 12 | get_excel_col_name | カラム番号(数字のみ)からExcelのカラム名を取得する 例:27列目はA1になる。 |
同上 |
| 13 | set_excel_font | 指定したシートにフォント(種類とサイズ)設定する Office互換ソフトを使用している場合は設定するフォントに注意して下さい。 |
同上 |
| 14 | set_excel_cell_width | 指定したシートのセルの列幅を設定する(見やすくするため) VB.NETでは列単位に一括設定できるのですが、残念ながらopenpyxlはセル単位になります。この為、全セルに対して列幅を設定しています。 |
同上 |
| 15 | get_convert_color_name | 指定された日本語表示の色名を英語表記のものに変換する。 Openpyxlでは日本語表示の色名は未サポートです。日本語表示の色名で設定するとエラーになります。日本語の色名を英語の色名にします。 |
VB.NETでは日本語表示色名を使用可能です。Openpyxlを使用 |
| 16 | set_excel_cell_edit_format2 | 生成したピボットテーブルのシート上のセルに、表示形式を設定する。 (数字のカンマ編集等):設定するセルが限定されたため別途作成する必要がありました。 |
Openpyxlを使用 |
| 17 | set_excel_cell_edit_format | 指定されたシートのセルに、表示形式を設定する(数字のカンマ編集等) | 同上 |
| 18 | set_excel_table | “指定されたシートにExcelのテーブルとして書式設定します。 テーブルとして書式設定すると1行毎に背景色を変えることができます。” | 同上 |
| 19 | get_timestamp_string | タイムスタンプの文字列を取得する。 | |
| 20 | check_bnd_files | 指定されたbndファイルに記述されているdef及びcsvファイルの存在チェックを行う。 | |
| 21 | check_csv_def_files | 指定されたdef及びcsvファイルの存在チェックを行う。 | |
| 22 | get_def_file_name | csv及びbndファイル名からdefファイル名を取得する | |
| 23 | get_sheet_name_with_maxlen | 指定された文字列からシート名として使用できる文字数の文字列を取得する | シート名に文字数制限があるため |
| 24 | send_mail | 指定されたsend_mail_dataの内容に従ってメール送信する本体 | |
これらは、sendmailメソッド以外は簡単なコードです。メインソースコードを汚さないようにしています。
2.4 XMLインポート関数(csvExcelImportXml.py)
XMLファイルをインポートしてパースするコードは長くなります。このため、メインプログラムから分離しています。(※DEBUGも分けたほうが便利だったため)
ソースコード内では同じ内容の繰返しが多くなります。一部サンプルを掲載します。
XMLパーサーで読込んだXMLファイルのデータをパースします。
※本来はDTD(Document Type Definition)を作成し、XMLファイルを検査終了した状態でパースするのが正解です。(Valid XML)
※今回のサンプルプログラムではDTDを省略しています。
今回はWellKnown XMLとしています。(よく知られたXML文)
上記のようにXMLをパースすると、XMLドキュメントのrootオブジェクトが返されます。(※今回のXMLはルートノードに属性はありません。)
返されたrootオブクエトから次に子ノードを取得します。子ノード取得後にノード名で判断し処理を行います。また、属性があれば属性を取得します。
上記は<head>を取得しているコードです。<head>には1つの子ノード<file>があり、属性も設定されています。 このXMLは、ノード名と属性名が重複しませんので、ノードデータや属性データは同じものとして取扱っています。(本来は問題なのですが。)
2.5 ピボット集計・グラフ描画関数(csvPivot.py)
タイトルの通りピボット集計・グラフ描画を行うクラスです。VB.NETでは実装を諦めましたが、Pythonでは簡単に実現できました。
2.5.1 ピボット集計
冒頭のaggfuncは、ピボット集計する際に指定する集計オプションです。(通常使用するのは件数もしくは合計ですが)
Pandasに用意されている関数でピボットオブジェクトを作成しています。
返されたピボットオブジェクト(sales)は、下記のように新しいシートとして既存のワークブックに追記します。
この後処理として作成したピボット集計は、あるシートに対して表示編集形式を設定しますが、ここでは説明を省略します。
2.5.2 グラフ描画
グラフ描画の前半です。
グラフ種類の引数をチェックしている部分です。
これだけのグラフ種類がありますので、通常使用であれば不足することはないと思います。
実際に描画しているコードはpx.chart.Referenceです。
また、縦軸・横軸の表示単位も設定できます。
マーカーの種類や描画する線の種類も設定できますが、上位アプリケーション側の負荷が高くなりますので、現在はコメントアウトしています。色々な事ができますが、どこまで実装すれば良いかが判らなくなったためです。(本当に使うのかも含めて)
グラフ作成時、Excelへの描画位置を指定します。
2.6 CSV->Excelメイン処理
下記表の流れで処理します。
| No. | 処理 | 備考 |
|---|---|---|
| 1 | CSVファイルの読込み | csvExcelUtilのload_csv_fileを実行 |
| 2 | Excelファイル出力 | 読込んだCSVファイルをExcelファイルに書き込む(DEBUGの都合) |
| 3 | Excelの各種設定 | フォント種、フォントサイズ、セル列幅、テーブル設定(※Excelを見やすくする設定です。) 設定する値はDEFファイルから取得します。 |
| 4 | Excelファイル出力 | Excelへの各種設定後のデータをEXCELファイルに書き込む。 |
| 5 | ピボット集計 | 前述の通り |
| 6 | グラフ描画 | 前述の通り |
| 7 | メール送信 | 後述 |
| 8 | FTP送信 | 後述 |
| 9 | ステータスファイルの書込み | ILERPG側で使用する情報を出力(成功・失敗の判定と出力したExcelファイル名) |
以降に、メール送信とftp送信を説明します。
2.6.1 CSVファイルの読込みとExcelファイル出力
ソースコードから抜粋した箇所で説明します。
- csv_util.load_csv_fileでCSVファイルをpandasライブラリーのDataFrameに読込みます。
- pandasライブラリーのto_excelで.xlsxファイルに出力します。出力する時にシート名やウィンドウ位置の指定ができます。
2.6.2 Excelの各種設定
- .xlsxファイルを読込みワークブック・オブジェクトを取得する
- ワークブック・オブジェクトからシート名でシートオブジェクトを取得する
- シートオブジェクトに表示形式・列幅・テーブル設定・フォントを設定する
- ワークブックを上書き保存する。
2.7 メール送信処理
Webで検索するとPythonのメールサンプルプログラムが多く見つかりますが、メールを正しく理解して書かれているコードは多くはありません。また、メールの日本語処理に関して誤解をしている方が多いようで、UTF-8は無条件に使用できると思いこんでいる人もいそうです。
日本語が使えるようになった黎明期からコンピューターに携わっている立場から言えば、動作はするけれども環境によっては文字化けするよ、と言いたくなるものがありました。UNICODEも複数のバージョンがあります。調べたところ、UNICODEバージョン 3.2でJIS X 0213の漢字文字は全て収録されたようです。
しかし、課題も残されています。
下記は、JIS X 0213利用者有志による、相互扶助を目的としたウェブサイト「x0213.org」に掲載されている記述です。
結合文字の問題
Unicodeでは、JIS X 0213の全ての文字に対し単一の符号位置が割り当てられているわけではありません。結合文字を用いて複数の符号位置の並びで表現できる文字については、単一の符号位置は与えられていません。
例えば、鼻濁音を表すのに使われる半濁点つきの「か」は、JIS X 0213では一つの独立した符号位置が与えられています(面区点 1-04-87)。しかしUnicodeでは、「か」+「合成用半濁点(U+309A)」という2つの符号位置の並びによって表現する必要があります。
互換漢字の問題
JIS X 0213で追加された漢字のいくつかは、UnicodeではCJK互換漢字として扱われています。これは、従来のCJK統合漢字に包摂されている字体のものです。
例えば、「神」の示へんが「ネ」でなく「示」の字体は、Unicodeでは包摂されています(つまり区別せず同じ符号位置で表す)。JIS X 0213はこれに独立した符号位置を与えましたが(面区点 1-89-28)、UnicodeではCJK互換漢字として、JIS X 0213との往復変換用との扱いで追加されています(U+FA19)。これは、Unicode正規化の処理を適用すると、対応するCJK統合漢字に移されてしまいます。
サロゲートペアの問題
JIS X 0213の一部の漢字はBMPでなく面02に追加されています。このため、UTF-16でサロゲートペアによって、4バイトで1文字を表します。
またUTF-8では漢字は通常3バイトですが、面02の漢字は4バイトの長さになります。UTF-8を扱うソフトウェアが4バイトを正しく扱えるか注意が必要です。
色々な考え方がありますが、私は日本語メールを送る時、ISO2022-jpを使用するようにしています。このISO202-jpはRFC2237 (Japanese Character Encoding for Internet Message)にも既定されています。電子メールで日本語を使用する時は、その環境で使用できるコードを調査してから使いましょうということです。また、メールにファイルを添付する場合は、関連するパラメータもセットしましょうということです。
現在の電子メールは、無条件にMIMEを使用すると考えていただいて問題はありません。ただし、MIMEを使用しますので、MIMEの情報は正しくセットしてください。
1. MIMEメッセージヘッダ
最初に、MIMEMutipart()でMIMEメッセージを作成します。
MIMEは複数のパートで構成され、それぞれのパートにタイプを持つため、パート毎にタイプ設定が必要です。
下記の例は、set_default_typeで省略時のタイプをセットしています。text/plainですので、通常の文字列(プレーンテキスト)の意味になります。
コラム
電子メールの黎明期は文字しか送れませんでした。かつASCIIコードのみ。当然日本語は使えません。現在の電子メールはベル研究所で開発されたUNIXに実装されたメールシステムから始まっています。その後、文字だけではなく画像や文書も送れるように拡張したのがMIME(RFC2045:多目的インターネット メール拡張機能)になります。
この拡張に日本人が日本語も使えるように提案した規格がISO2022になります。(日本語ですから当然使用するのも日本人です。)。その後、OS開発ベンダーが各国毎にOSを用意する労力を軽減するため全世界で単一バイナリーですむようにUNICODEが制定された記憶があります。
From, Cc, Bcc, Subject, Dateをセットします。Subjectには日本語が含まれますのでHeader関数のエンコード指定でISO-2022を設定しています。
2. MIMEボディ
本文のパートを作成し、1.で作成したMIMEメッセージに追加します。
日本語メッセージはISO-2022JPコード変換後、Base64でエンコードします。日本語を扱う場合、通常はこのような手続きを行います。
※現状では8ビットデータも通過するようになっていますが、少し前までメールの世界は7ビットデータしか通過できませんでした。
次にファイルを添付する場合も新たなパート(下記のサンプルでは、octet-stream:8ビットデータ)を作成しBase64エンコード後にMIMEメッセージに追加しています。(※Base64は7ビットコードのみ使用するエンコードです。)
なお、添付ファイル名は、パートのヘッダー:Content-Dispotionのattachment属性にセットします。日本語ファイル名を使用する場合はエンコードが必須です。
3. メール送信
実際のメール送信は、コード上はSSL(実際はTLS)であっても、、メールサーバーによって接続の関数名が異なります。
現在はSMTPに接続する場合、587ポート(Submissionポート)になり、このポートを使用する場合には認証処理が必要です。
認証方法は複数あり、Pythonのメール関数では適切な認証を自動的に判断し使用します。ログイン後、send_messageで送信します。
送信終了後、quitにてメールサーバー接続を終了します。
ftp送信処理
ftpは、「サーバー接続」、「ディレクトリー変更」、「バイナリーファイル送信」、「切断」のステップで行います。なお、Pythonのftpライブラリーは、日本語名を使用すると文字化けしてエラーとなります。ライブラリー名称は英大文字にするほうが無難です。
ファイヤーウォールを介してftpサーバーに接続する場合は、Passiveモードで接続する必要があります。Passiveモードで接続しないと、ファイヤーウォールを通過できません。
3. PASE環境でのTIPS
3.1 Windows<->PASE環境の相違
当たり前の話ですが、PASE環境はAIX環境(いわゆるUNIX環境)です。UNIXは英大文字小文字を区別しますので、File Not Foundにならないようにコードを書く時から注意しましょう。
3.2 PATH変数の設定
V7.5TR5のPASEのオープンパッケージで導入されたソフトは、/QOpenSys/pkgs/binにシンボリックリンクが貼られます。
しかし、PATH変数は/QOpenSys/pkgs/binに含まれていません。UNIXでの/etc/profileファイルにあたる/QOpenSys/etc/profileファイルの最後にexport PATH=$PATH:/QOpenSys/pkgs/binを追記します。
3.3 文字コード体系
古くからIBMiを使用しているユーザーは、外字を使用していると思います。本来は、外部にデータを渡す場合は、外字を使用しないことが最善です。しかし、使用しない状態にする労力が大きいため、外字をそのまま使用しているユーザーも多いかと思います。
Pythonのスプリクトを書く場合は、UTF-8が推奨されています。Shift-JISの漢字が全てUTF-8で使用できるとは限りません。特に、外字は要注意です。
使用するCSVデータに外字が含まれる場合、データ解析ライブラリー(pandas)での読込み時に、文字コード変換エラーとなります。
ファイルOPENに文字コード指定(encoding=”cp932”)とともに、文字変換エラー無視(errors=”ignore”)パラメータを指定する事で読込み可能となります。弊社環境では、Excel変換したデータに外字が含まれていても正常に表示されました。(※Windowsには外字エディタで外字フォントを取込済み)
3.4 Pythonのネーミングルール
ネーミングルールに従う事で、エディタもしくはIDEがアシストしてくれるので便利です。
なお、今回のサンプルプログラムでは、下記ルールの全てを遵守しているわけではありません。
| 対象 | ルール | 例 |
|---|---|---|
| パッケージ | 全て小文字なるべく短くアンダースコア非推奨 | requests |
| モジュール | 全小文字なるべく短くアンダースコア可 | load_csv |
| クラス | 最初大文字+大文字区切り | MyClass |
| 例外 | 最初大文字+大文字区切り | MyExectopnError |
| 型変数 | 最初大文字+大文字区切り | MyNum |
| メソッド | 全小文字+アンダースコア区切り | load_csv_file |
| 関数 | 全小文字+アンダースコア区切り | my_function |
| 変数 | 全小文字+アンダースコア区切り | my_instance_var |
| 定数 | 全大文字+アンダースコア区切り | MY_CONST_VAL1 |
3.5 ロケール文字の相違
PASE環境でロケールを locale.setlocale(locale.LC_TIME, ‘ja_JP.UTF-8’) にすると、UNSUPPORTエラーとなります。
“Ja”を大文字で指定locale.setlocale(locale.LC_TIME, ‘JA_JP.UTF-8’)することで、エラーを回避できます。
3.6 FTP使用時の注意事項
生成したExcelファイルをFTP送信する場合、出力ファイル名にシングルバイト英文字のみを使用してください。
Pythonのftplibでの制約。ftpのgetでUnbale to FIND NAMEとなります。
3.7 メソッドとプロパティ
wb = openpyxl.load_workbook(“ファイル名”)でワークブックを読込んだ場合、wb.saveと記述すると何も機能せず、エラーにもなりません。 Wb.save()と記述すると、正常に保存されます。
3.8 PASE環境のシェル及びシェルスクリプト
PASE環境は、/QOpenSys/usr/bin/shのシェルが動作します。リンクを確認するとksh(Kornシェル)が動作する設定になっています。
(※AIX標準シェルはkshです。)
bashではありませんので、シェルスプリクトを記述する場合は注意してください。
また、シェルスプリクトを作成する場合、改行コードはCRLFではなくLFです。
改行コードがCRLFのシェルスクリプトを実行すると、理解できない行で「^Mが見つかりません」などのエラーとなります(^Mは改行コード)。
ちなみに、Windowsのwsl(私の環境はUbuntu)では、改行コードがCRLFでもエラーになりませんので注意してください。
PASE環境でCRLFからLFに変換しようとしても、ACSにおける¥とバックスラッシュの設定により画面上では双方とも¥で表示されるので見分けが付きません。このため、sedやtrコマンドでCRの削除が上手く動作しませんので注意して下さい。
(ACSの設定を確認したところ、バックスラッシュが正しく入力できる設定にはなっていたのですが、PASE環境には上手く入力できませんでした。)Windowsのエディタで、入力改行をLFに設定してシェルスクリプトを作成するのが無難です。(PASE環境へftpする際はバイナリーモードで転送します。)
4. 環境構築
サンプルプログラムは、IBMi側とWindows側にセットするものがあります。
Windows側は、Excelファイルへの変換後に、Windows側にFTPでダウンロードして、Excelを開くバッチファイルのみです。
| No. | 環境 | 作業 | 補足 |
|---|---|---|---|
| 1 | Windows | C:\TESTを作成し,CSVEXCELFTP.BATをコピーする | パラメータファイルに指定されているフォルダーに配置する |
| 2 | IBMi通常環境 | IBM_I_EXCELLIB_V7R3M0.savfをパソコンからIBMiにFTPバイナリー転送後、RSTLIBで復元します。 | |
| 3 | IBMiPASE環境 | エミュレータのホストコードページ=1399でサインオン後、以下の処理を実行 ①CHGJOBCCSID(1399) ②CALLQP2TERM←PASE環境に入る ③mkdir-p/opt/excel |
|
| 4 | IBMiPASE環境 | パソコンからsavf以外のファイルをPASE環境の/opt/excelにftpバイナリー転送する |
5. 感想
- Windows環境で作成する場合、ファイル名の英大文字小文字を注意してスプリクトを書いてください。
- 私は、Windows環境でソースコードを書き、Windowsでテストしました。上記の英大文字小文字さえ注意すれば、違和感なくPASE環境で動作します。
- Pythonには充実したライブラリー(モジュール)が揃っています。(PDFも作成できます。)
- CLからもqpl2shellでPythonスプリクトが実行できますので、RPGでの実行が不可能な内容を組み込めます。
- Pythonのライブラリーには、様々な機能があります。
6. 最後に
いかがでしたでしょうか。結構な事ができるのだ、と感じていただけましたでしょうか。
昨今は、ソリューション導入が主流になっています。しかしながら、中小企業は一般的に予算が厳しい状況ですので、システム担当者ができる範囲で対応することが大事ではないかと思うのです。
もちろん、勉強しなくてはなりません。今は色々な情報をWebで簡単に入手できます。一から勉強するのではなく、既にある事例を参考にすれば良いと思います。従いまして、私は今までやってきた事を、このようにWebで発信して、お互いに共有していけたら、と考えています。












日本アイ・ビー・エム株式会社が毎月開催しているIBM Powerユーザーのための自由な語り場「IBM Power Salon」(月1回、第二水曜日の朝9時から開催)をご存じでしょうか?
IBM Power Salonのページはこちら
2023年1月11日に開催された第14回の、株式会社 電業様による「惜しみなく共有しちゃいます、根っからのエンジニアが語る、IBM i 内製化のリアル。」では、既存IBM i環境を見事に活用し、自作でIBM i と自動倉庫をつなげDXを実現された素晴らしい事例が披露されました。
https://video.ibm.com/recorded/132452956
とはいえ、1時間の講演時間では語りきれなかった詳細は、きっと他のIBM i ユーザーの方にも参考になるはず!ということで、株式会社電業 総務部 竹本伸明様に“内製DX”について具体的にご説明いただきます。読めばきっと、「IBM i でここまでできるんだ!」と目から鱗が落ちるはずです!