Google Documentを使って履歴書自動出力機能を作った話

とある案件で、登録プロフィールをもとに履歴書を出力したいという要望がありました。
apiにプロフ情報を渡してやると、pdfのデータURLが返ってくるという具合です。
あんまり頻繁にやるようなことでもないですし備忘録の意味と、やってみたら案外できたという部分で誰かの助けになればと思って記録しておきます。

概念

履歴書の作成ということで、始めに.doc.xlsxのテンプレートがあって、それをパースして書き換えるようなライブラリが必要なイメージがありました。
ぱっと見あんまり良さげなものがなさそう(あっても難解)でそもそも別の方法がないかと思っていたところ、
GoogleSpreadSheetに今回欲しい機能が揃っていて、APIで操作できるというところに行き当たりました。

  1. スプシに履歴書テンプレートを用意
  2. {name}, {address}といった感じでテンプレート言語的なプレースホルダを配置
  3. APIでテンプレートを読み込んで、シンプルに文字列の置換で情報を記入
  4. 別ファイルとしてスプシにアップロード
  5. PDFにエクスポート

のような形で今回の要件は満たせるはずです。
問題になりそうなのはスプシのアクセス頻度制限で、その辺りも調査が必要になってきます。

履歴書テンプレート

履歴書.tpl
こんな感じで用意しておく。
{name}とか{nameFurigana}などとなっている箇所を置換していく。

Google Apiの使用

Google Apiのnode.js向けクライアント経由でapiを叩きます。

  1. 予め用意したテンプレートファイルを複製
  2. 複製したファイルに必要事項を記入
  3. PDFとしてダウンロード
  4. 複製ファイルを削除

の手順で操作します。

テンプレートファイルの複製

1
2
3
4
5
6
7
8
9
10
const client = await new auth.GoogleAuth({
keyFilename: '認証情報のあるファイルパス',
scopes: '操作範囲の要求',
}).getClient();

const drive = new DriveOperator(
drive({ version: 'v3', auth: client })
);

await drive.files.copy({ fileId }).then(r => r.data);

文字列置換により、必要事項を記入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const client = await new auth.GoogleAuth({
keyFilename: '認証情報のあるファイルパス',
scopes: '操作範囲の要求',
}).getClient();

const doc = new DocsOperator(
docs({ version: 'v1', auth: client })
);

const dictionary = {
'これが': 'こうじゃ'
};
const requests = Object.entries(dictionary).map(([before, after]) => ({
replaceAllText: {
replaceText: `${after}`,
containsText: {
matchCase: true,
text: `{${before}}`
}
}
}));

await doc.documents.batchUpdate({ 'ドキュメントID', requestBody: { requests }}).then(r => r.data);

pdfのバイナリデータをダウンロード

1
2
3
4
5
6
7
8
9
10
const buf = await drive.files.export(
{
fileId: 'ドキュメントID: ブラウザでドキュメントを開いた時、urlに含まれるID',
mimeType: 'application/pdf',
},
{
responseType: 'arraybuffer' // 後述
}
);
return buf.data as ArrayBuffer;

複製ファイルを削除

1
await drive.files.delete({ fileId: 'ドキュメントID' });

GoogleDocumentにブラウザでアクセスして、webインターフェースからPDFにエクスポートした場合と、上記のAPIで取得したデータに差が見られました。

前者

後者

同じ部分を切り取ってみました。
同じファイルを取得できているっぽいのですが、どうも文字化けというか、文字コードがバグっているような雰囲気が見られます。

真実はいつも一つ

GoogleApiクライアントのコードをガン堀りひたすら辿っていくと、GAxiosというライブラリに行き着きます。
その中の一部分がこちら。

opts.responseTypeというパラメータによってデータの受け取り方式を変えているみたいです。
こいつを辿っていくと、先のサンプルコードにて// 後述とした部分で設定される部分の模様です。

1
responseType: 'arraybuffer'

今回のことでAPI自体のドキュメントや関連記事は結構漁ったものの手がかりは得られず、事件現場に戻ってくる犯人のようにGoogleApiクライアントのREADMEを開けば

1
2
3
4
5
6
7
8
9
For example:

const res = await drive.files.export({
fileId: 'asxKJod9s79', // A Google Doc
mimeType: 'application/pdf'
}, {
// Make sure we get the binary data
responseType: 'stream'
});

ってちょろっと書いてある。
誰が分かんねん。

google関連のドキュメントってシンプルに纏まってる感じがするんですが、必要なことがたまに抜けてるような印象するの僕だけですかね…?
amazon系のドキュメントは逆に網羅的すぎてなんもわからんとなるので、ええ塩梅でお願いしたいですね。