Docurainを使って帳票を出力する場合、そのデータの多くは社内データベースからの出力になるでしょう。しかし、外部サービスと組み合わせることで、データをよりダイナミックに変更できます。プログラムから帳票生成を実行すれば、そういった操作も簡単です。
今回はごく簡単な例として、住所をジオコーディングして位置情報に変換し、さらにその位置情報を使って静的な地図画像を取得します。帳票に限りませんが、PDFの中に外部サービスの情報を埋め込む際のテクニックの1つとして参考にしてください。
データはダミーで生成
今回のデータは個人情報テストデータジェネレーターを使ってダミーで生成しています。
名前、社名、郵便番号と住所を生成し、Googleスプレッドシートに記述しています。
GoogleスプレッドシートからデータをJSONで取得
Googleスプレッドシートに記述しているデータをJSON化するためにSSSAPIを利用しています。SSSAPIはGoogleスプレッドシートを読み込み専用のAPIにしてくれるサービスになります。
先ほどのデータは次のようなURLで取得できます。
https://api.sssapi.app/AAAAAAAA
アクセスすると、以下のようなJSONが取得できます。
[ { "name": "廣瀬 勝久", "zipcode": "566-5125", "address": "大阪府大阪市淀川区加島1-2-604", "company": "有限会社長谷川商店" }, : { "name": "福山 知香子", "zipcode": "804-0049", "address": "福岡県福岡市早良区飯倉1-5-21", "company": "株式会社佐藤工務店" } ]
住所を位置情報に変換する
住所を位置情報に変換することをジオコーディングと言いますが、今回はシンプル ジオコーディング 実験 – 位置参照技術を用いたツールとユーティリティを利用しています。こちらは登録なしで利用できますが、あくまでも実験的なものになりますので、ビジネスで利用される際には他の地図サービスを利用した方が良いでしょう。このサービスでジオコーディングを行う際には、次のようなURLになります。
https://geocode.csis.u-tokyo.ac.jp/cgi-bin/simple_geocode.cgi?addr=(住所)
これで、次のような結果が得られます。
<results> <query>目黒区駒場4-6-1</query> <geodetic>wgs1984</geodetic> <iConf>5</iConf> <converted>目黒区駒場4-6-</converted> <candidate> <address>東京都/目黒区/駒場/四丁目/6番</address> <longitude>139.676651</longitude> <latitude>35.663120</latitude> <iLvl>7</iLvl> </candidate> </results>
今回必要なのは、この結果の中の latitude
(緯度)と longitude
(経度)になります。
位置情報から静的な地図画像を取得する
最後に位置情報から地図画像を取得する処理ですが、これはMapboxを利用します。MapboxはOpenStreetMapを使った地図サービスになります。静的画像APIのURLはStatic Images API | Playground | Mapboxを使って生成できます。
例えばURLは次のようなものです。
https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/(経度),(緯度),16,0/300x300?access_token=(アクセストークン)
これを実行すると、PNG画像で該当部分の地図が取得できます。
以上で帳票に掲載する素材が揃いました。
帳票の設計
今回はシンプルな帳票となっています。注意点として、画像はシェイプを使って描画する枠を作成しておきます。そして、その内容を #IMAGE($contact.map)
としておきます。
A1とA2セルでは次のように記述しておき、データを繰り返し処理します。
#set($e=$ROOT) #foreach($contact in $e.contacts)
A16あたりで繰り返し処理の終了指定を忘れずに行っておきましょう。
#end
コードを書く
では外部APIの実行を含めたDocurainの帳票生成プログラムを作成していきます。今回はRubyを利用しています。
利用するライブラリのインストール
HTTP用のライブラリを含め、次のライブラリを使っています。
- faraday
- faraday_middleware
- ruby-filemagic
適当なディレクトリでGemfileを作成します。
$ bundle init Writing new Gemfile to /path/to/dir/Gemfile
このファイルを編集します。今回は次のように変更しています。
# frozen_string_literal: true source "https://rubygems.org" git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } # gem "rails" gem "faraday" gem "faraday_middleware" gem "ruby-filemagic"
そしてライブラリをインストールします。
$ bundle install
スクリプトの作成
適当なファイル(今回はupload.rbとしています)を作成し、ライブラリを読み込みます。
# Docurainへのアクセス用 require 'faraday' require 'faraday_middleware' require 'ruby-filemagic' # 他のAPIへのアクセス用 require 'open-uri' require 'uri' require 'json' # 地図画像をBase64エンコードする際に利用 require 'base64'
次にSSSAPIへのアクセス用関数を用意します。実際のURLはあなたが使っているものに書き換えてください。
def get_contact JSON.parse open("https://api.sssapi.app/AAAAA").read end
次に地図画像を得る処理です。まず最初に住所から位置情報に変換し、その後MapboxのAPIを使って静的な地図画像を取得します。Docurainで画像を利用する際にはDataURI形式で投稿する必要があります。今回はPNG画像と決め打ちにしていますが、画像形式に合わせて適宜変更してください。
def get_map(address) # 住所から位置情報に変換する url = "https://geocode.csis.u-tokyo.ac.jp/cgi-bin/simple_geocode.cgi?addr=#{URI.encode(address)}" results = open(url).read # XMLですが、今回は正規表現で緯度経度を取得します longitude = results.match(/.*<longitude>(.*)<\/longitude>.*/m)[1] latitude = results.match(/.*<latitude>(.*)<\/latitude>.*/m)[1] # Mapboxにアクセスして、静的地図画像を取得します access_token = "YOUR_MAPBOX_ACCESS_TOKEN" url = "https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/#{longitude},#{latitude},16,0/300x300?access_token=#{access_token}" results = open(url).read # バイナリデータなのでBase64エンコードし、DataURI形式にします "data:image/png;base64,#{Base64.encode64(results)}".gsub(/\n/, '') end
DocurainのAPIを実行する処理
次にDocurainの実行部分を作成します。ここからは api_call 関数の内容です。
# 帳票レンダリング(インスタント)APIコール def api_call # この中に書きます end
まず必要な変数を準備します。
token = 'YOUR_API_TOKEN' # Docurain API トークン out_type = 'pdf' # 出力形式 template_path = './template.xlsx' # テンプレートファイルパス entity_json = open('./entity.json').read # テンプレート置き換え文字列のJSON path = "/api/instant/#{out_type}" # APIのパス
次にテンプレートファイルのコンテンツタイプを取得します。
# テンプレートファイルのContent-Typeを取得 template_content_type = FileMagic.new(FileMagic::MAGIC_MIME).file(template_path, true)
これらの情報を使ってリクエストボディを作ります。
# リクエストボディの作成 params = { template: Faraday::FilePart.new(template_path, template_content_type), entity: Faraday::ParamPart.new(entity_json, 'application/json') }
後はFaraday(HTTPクライアント)を使って、APIを実行します。
# APIを実行する conn = Faraday.new(:url => 'https://api.docurain.jp') do |builder| builder.request :multipart builder.adapter :net_http end # 認証ヘッダーの設定 conn.headers['Authorization'] = "token #{token}" # 実行結果(レスポンス)を返す conn.post(path, params)
これでAPI呼び出し処理の完成です。関数全体の内容は次のようになります。
# 帳票レンダリング(インスタント)APIコール # 引数のentiry_jsonはテンプレート置き換え文字列のJSON def api_call(entiry_json) token = 'YOUR_API_TOKEN' # Docurain API トークン out_type = 'pdf' # 出力形式 template_path = './contact.xlsx' # テンプレートファイルパス path = "/api/instant/#{out_type}" # APIのパス # テンプレートファイルのContent-Typeを取得 template_content_type = FileMagic.new(FileMagic::MAGIC_MIME).file(template_path, true) # リクエストボディの作成 params = { template: Faraday::FilePart.new(template_path, template_content_type), entity: Faraday::ParamPart.new(entity_json, 'application/json') } # APIを実行する conn = Faraday.new(:url => 'https://api.docurain.jp') do |builder| builder.request :multipart builder.adapter :net_http end # 認証ヘッダーの設定 conn.headers['Authorization'] = "token #{token}" # 実行結果(レスポンス)を返す conn.post(path, params) end
レスポンスを判定する
次にレスポンスの内容を判定して、レポートをファイル出力します。ここからは res_handle 関数の内容です。
# レスポンスハンドリング(適宜必要なハンドリングを行ってください) def res_handle(res) # この中に書きます end
まずHTTPステータスが200(正常終了)以外の場合はエラーメッセージを出して終了します。
if res.status != 200 # エラー判定 puts res.body # エラーメッセージを返して終了 return end
次にレスポンスのContent-Typeから、出力するファイルの拡張子を指定します。
# 正常時、カレントディレクトリにファイル保存 # content-typeから拡張子に変換 extensions = { 'application/pdf' => 'pdf', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', 'application/vnd.ms-excel' => 'xls', 'image/svg+xml' => 'svg', 'image/png' => 'png', 'image/jpeg' => 'jpg', 'image/gif' => 'gif', } content_type = res.headers['content-type'] ext = extensions[content_type]
後はファイル名(今回は test に固定)を決めて、ファイル書き出しします。
# ファイル名を決めて書き出し file_name = "test.#{ext}" filePath = "#{Dir.pwd}/#{file_name}"; File.binwrite(filePath, res.body) # メッセージを出力 puts "saved : #{filePath}"
この関数の全体像は次のようになります。
# レスポンスハンドリング(適宜必要なハンドリングを行ってください) def res_handle(res) if res.status != 200 # エラー判定 puts res.body # エラーメッセージを返して終了 return end # 正常時、カレントディレクトリにファイル保存 # content-typeから拡張子に変換 extensions = { 'application/pdf' => 'pdf', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' => 'xlsx', 'application/vnd.ms-excel' => 'xls', 'image/svg+xml' => 'svg', 'image/png' => 'png', 'image/jpeg' => 'jpg', 'image/gif' => 'gif', } content_type = res.headers['content-type'] ext = extensions[content_type] # ファイル名を決めて書き出し file_name = "test.#{ext}" filePath = "#{Dir.pwd}/#{file_name}"; File.binwrite(filePath, res.body) # メッセージを出力 puts "saved : #{filePath}" end
実行する
後は関数を順番に実行していきます。
contacts = get_contact contacts.each do | contact | # mapの中に地図データ(DataURI形式)を入れる contact["map"] = get_map(contact["address"]) end # テンプレート書き換え用パラメータの準備 params = {} params[:contacts] = contacts res = api_call(params.to_json) # APIコール res_handle(res) # レスポンスハンドリング
出力結果
実行結果です。各コンタクトごとにページが分かれ、入力した情報と地図が埋め込まれています。
まとめ
今回は外部サービスを多用しながら、Docurainの出力を動的に変更する例を解説しました。ここまで使わずとも、外部サービスを使うことで社内データにはない情報を帳票に埋め込めるようになります。地図やWebサイトのサムネイル、アクセス解析結果など帳票やレポートをグレードアップさせるためにも、外部サービスを利用してください。