🎓

サーバーレスではじめる大学生活(時間割管理)

2023/04/15に公開

大学の時間割がPDFしかなくてカレンダー入れるのが一々手間だったので自動化したノウハウ

前提

公開されている時間割ではないので、どういうPDFなのかぼかして記載しています。
そのためパッとしない説明が多いですがご了承ください。

アーキテクチャ

システムは

  • Pre processor
    • PDFからテキスト情報とカラー情報を抽出し、JSONにしてR2にupload
  • API
    • URLから対象のJSONを特定し、GETパラメーターを元に特定のコマをicsに変換し返す

の2つに分かれています。

困ったこと

PDFのテキストデータが構造化されていない

今回対応したPDFにはExcelのような表があり、その中に「x日のn時限目はyy教科」というような情報が含まれている。
これを抽出したいわけだが、

Portable Document Formatは、デジタルデバイス上でアプリケーションやOS、ハードウェアに依存せず文章や図版を表示するために開発され、ISO 32000で国際標準化された電子文書ファイル形式である。 by Wikipedia

であることから、当然RDBのように構造化されているわけではない。
ではどうするかというと

  1. 隣接しているテキスト同士を結合 / セル情報に変換する
  2. 座標からセル情報に変換 / テキスト統合

の二択であるが、前者は上手くいかない。
見出しなどで文字同士が離れていたり、逆に隣接しすぎて別の文字列だと判断出来なかったりと、色々面倒事が多いからである。[1]
そのため今回は(2)を採用した。[2]

具体的には「座標aから座標b間にあるテキストは一つのテキスト(=セル)としてみなす」という実装になっている。
https://github.com/come25136/scper/blob/f93f6e6daee79eb5062b61c6aaafaff4ce6426a5/pre_processor/src/nichiyaku/pdf-to-object.ts#L91

学年毎に色分けされている

セルの背景色で学年識別できるようになっている

pdfjs-distというライブラリを使用してPDFデータを読み取っているが、色情報を取得する方法が分からなかった。
(恐らく図形の背景色みたいな扱いになっていると思われる)
https://www.npmjs.com/package/pdfjs-dist

そこで今回はPDFを一旦canvasに描画して、特定のピクセル情報を取得する方針にした。
幸いにもpdfjsにcanvasに描画する関数が用意されているので、それを利用し色情報を取得することが出来た。
https://github.com/come25136/scper/blob/f93f6e6daee79eb5062b61c6aaafaff4ce6426a5/pre_processor/src/nichiyaku/pdf-to-object.ts#L310

コマごとに抽出

icsに変換後R2にアップロードして終わり!と思っていたが...
本システムを使いたい人によって取っているコマが違うだろう。
そのため一度Cloud Runを挟み、欲しいコマのみ抽出できるようにした。
GETパラメーターとArray.filter使ってるだけなので割愛。コード見てください。
https://github.com/come25136/scper/blob/f93f6e6daee79eb5062b61c6aaafaff4ce6426a5/api/src/index.ts#L143

お金周り

自分だけならまだしも、他人も使えるようにしてしまったせいでアクセス数が読めない。
そのため無料枠が多いCloudFlare R2Cloud Runを採用した。
実はCloud Function Gen 2も使っていましたが、Artifact Registry周り(特にCloud Storage)で料金が発生してしまったため、Docker Hub経由のCloud Runに変更しました。

また、Cloud Runだけだとコールドスタートでレスポンス遅いので、間にCloudFlare CDN挟んで高速化しました。
どちらかといえば無駄にCloud Run実行されるのを防ぐという意味合いが強いけど。

おわりに

PDF扱う人の何かの参考になれば幸いです。

余談ですがPDFの保存先にGoogle Driveを選んだ理由は、大学行ってるのが彼女なので使い勝手の面でGoogle Driveになりました。

リポジトリ

https://github.com/come25136/scper

脚注
  1. 見た目は一つのテキストでもデータ的には別れていたりする ↩︎

  2. 岡山県 新型コロナウイルス感染症対策サイトを作った際も同様の方法でやった
    (昔は統計情報がPDFのみだったので) ↩︎

Discussion