Open21

公文書作成の自動化を検討する

kiaikiai

端的に言えば、

  1. MS Word / Excel が担っていることを TeX にやらせたい → Overleaf 有料版にしかない機能があり、断念
  2. そのUI/UXを Overleaf に任せたい → (同上)
  3. その裏でテンプレートエンジンとして動く何らかの設計をどうやって行くか考えたい

というのがこの Scrap です、なにとぞ


2025-01-04 更新:Overleaf 導入断念

kiaikiai

MS Word / Excel が担っていることを TeX にやらせたい

word / Excel を Python から読み込んで書き出すアプローチも考えたけど、直感的になんかイマイチ筋が悪いよなぁと感じていた

で、予想通りそのように自動化できる仕組みもあったんだけど、

  1. template の扱い
  2. ロジックとビューの分離
  3. 共同編集
  4. その他の利便性

等々を考えて、ゆくゆく無理が生じそうだな~と思ったので

  • レイアウト(組版処理)は TeX (ないしその派生物)
  • templating は python(で十分のはず、だけど枯れたやつにした方がいいのかな)

という方針でいったん Fix

kiaikiai

SATySFi やら Twight やら、LaTeX の次となりそうな候補はあれど、実際に採用できるかというとまだそこまでの段階にはないようだ

  1. template は mustache から .tex 系のあれこれを見に行く
  2. 元データは .xlsx / .csv / .json 等から抜き出す(まずは python で実装)
  3. Overleaf で確定
  4. converter がほしい (.docx to TeX and vice versa)

templating のスクリプトを搭載したサーバを作るが、方針は2つある:

  1. ユーザがパスを指定 or アップロード することで、template & data を渡す
  2. サーバに常駐デーモンを置き、任意のフォルダ配下を常に監視する

タイミングが任意になって負荷が少ないのが前者だが、常に攻撃の可能性に晒される

負荷が常に一定程度かかるのが後者だが、セキュリティ的には安心?

……と思ったけど、ファイルサーバが膨大になったら簡単に詰むため、後者は採用できないっぽい(ので、1の方針で考えます)

kiaikiai

くわえて、templating のデータソースはファイルだけじゃなくてAPIを叩ける仕組みにしたい

例えば excel に個人情報を載せて流し込むのはよくない(それは個人情報名簿になってしまいリスクが危ない)
これを人事上のIDに置き換えれば、excel ファイルの所有者は見られないが、サーバ側では見られる(サーバ上に認証情報を持つ必要あり)ということもできる
API を提供するサービスを別途実装する必要があるが、このアーキテクチャのほうが情報保証の観点では良いのではないか?

kiaikiai

UI/UXを Overleaf に任せたい

公務員全員に記法を理解して Overleaf 使えとは言わない、言えない

が、個々人の環境設定まで面倒見て組織ルールに合わせろってのは土台無理があるので、内部ネットワークがあってブラウザが開ければそこで WYSIWIG っぽくエディタが用意できていると嬉しい

ライセンス的な問題が生じるかもしれないことは承知の上で、まず第一の選択肢として考えられるのは Overleaf であろう(こういうニッチのデファクトスタンダードなため)
どうにか稟議回して上司の機嫌を取り、内部ネットワーク上にサーバを立てれば、そこがまず最初の足がかりになるはず

これだけだと Overleaf を使う旨味がないのだが、ここでは「テンプレート」を「共同編集」できるというのが差別化であり、最大の利点であり、コペルニクス的転回となる
(なんだったら行指向のヴァージョン管理ができるようにのだが、そこはまぁおいといて)

kiaikiai

全然忘れてたけど、ユーザの権限管理とかアカウンティングとかそっちの管理系の問題が生じてくる気がする

ハナから扱わない人間には触らせないホワイトリスト的な運用がいいのか、都合が悪い人物だけ先に禁止するブラックリスト方式がいいのか
というかそもそもログイン管理は組織のものを流用できるのか、等々……

このあたりをうまく設計できれば、お硬い上司も説得しやすくなるかもしれない、要検討

kiaikiai

文書作成・管理と、実際に templating (値の流し込み) をやる場所を切り分けてやればそれも解決しそうだ

ファイル自体は共有ファイルサーバ(AD等)に置いて、Overleaf サーバ / Templating サーバがそれぞれ独立して稼働するようにすればよい

ファイルを見に行かなくとも、ローカルからうpするのでもよい
仕組みはいろいろそうてできる

kiaikiai

テンプレートエンジンとして動く何らかの設計

overleaf で作成したテンプレートと、そこに inject したい変数とその値の一覧ファイル(csv, xlsx, etc.) をなんらかのトリガーでもって処理して、pdf なり最終成果物として出力したい

言語選定として、書きやすいとかライブラリ豊富とかで Python を選びたい気持ちと、将来的に負債になってほしくないから枯れた技術を採用すべきという気持ち、心が2つある~~
そこまでデカくならない・しないと心に決めて Python にするか、保守性とか管理を諦めてC++とかそっちにするか、悩みは尽きない

単にファイル読み込んで文字列置換でしかないわけで、そういう簡易な仕組みを追うだけだからなんでもいいでしょという話にはなりそうだ


なんらかのトリガー について、多くの役所は AD 採用で動いているだろうから、規定フォルダの内容変更を検知したら処理が走るようにすればいいと思う
あるいは、ADへの権限周りで対策が重くて困るとなったら、面倒だが手作業でファイルのアップロードとダウンロードをやるという感じから始めてもいい
一つのテンプレと複数のデータ一覧をいれたら、完成済みの複数データが zip で帰って来るとなれば、これまでよりはずっと楽ができそうだ

kiaikiai

Python とかで正規表現頑張って自前実装する、というアプローチもあるけど、とりあえずのモックアップとしては動いているものを見せる必要があり、それは先人に倣えばいいのではないかと思い至る

すなわち、mustache を使って Templating を実現することとした

自分は他にも jinja2Eta.js を使ったことがあるが、どちらも言語に固有なもので縛られてしまう気がして避けたくなった(本当に自分が想定する規模で使うなら、速度はかなり要求水準が高いため; 信頼性やメンテ可能性も考慮する必要があった)

拡張性がそこまで高くない一方で、すでにほぼ枯れた技術となっているという点でも、 mustache をとりあえず採用することとなった

kiaikiai

既存の文書は再利用したい

既存の財物をそのまま投げ捨てるのは流石にな~と思って調べていたら、docx2tex というプロジェクトがあるらしい

ローカルで動くし、これを食わせてたたき台とすれば、今まで利用してきた word 文書も少なからず流用できる可能性があるかも? ← 要検証

kiaikiai

エントリーポイント

template & data を受け取って mustache で処理し、最終成果物(PDF)を zip して渡す(ダウンロードさせる or 共有ファイルサーバにうpする)

問題は、Templating サーバがどうやって受け取るか?どういうトリガーで発火して処理が始まるのか?

Ans. ファイル選択 or パスを入力するページを用意すれば良さそう

クソデカファイルになるとうpがきつくなる可能性もあるので、パス指定はほしい(ファイルの移動が厳しいというパターンも想定)

(パス指定だとUI/UX的によろしくないことも起こりうるので、そのへんをフォローする仕組みも必要)


これらのファイルが妥当なものか検証して結果を通知する必要がある

加えて、流し込むことになるデータの表を提示してあげると親切(データ構造上は妥当だけど、データ表示上は破綻している、というパターンはきっとある)
で、最終的に「出力!」を押して実行すると、いくつかのPDFがzipになってダウンロードリンクが表示される(あるいは、そこまでのファイルパスが表示される)

kiaikiai

/upload /confirm /progress /download の 4つほどエンドポイントが生えていれば良さそう

また、/confirm の段階で元データからハッシュ等出して ID を払い出せば、templating 中にページを閉じても /download に対して id を渡してやれば(つまりハイパーリンクを払い出せば)、一時的にページを閉じてもサーバ側の処理を追従できる
(アカウンティングしてあればメール等を通じて通知できる、あるいはGETパラメタにidが残れば、ブラウザの履歴で追いかけることもできる)

あるいはページを閉じなければ、Web通知で処理の完了を伝えられるかもしれない

やりかたはたくさんある

kiaikiai

全体イメージ

  1. template.tex を共同編集する Overleaf サーバ
  2. 元データとテンプレートからPDFをもらうフロントエンドサーバ
  3. 元データとテンプレートを受け取って rendering するバックエンドサーバ

=> 実は、1と3を同じところにしたほうがいいかも?(わけると面倒なので+わけても処理系は一緒なので?)


markdown でもそうだけど、実はファイルそれ自体以外にも依存するファイル(画像とか latexmkrcとか)があり、それを補う方法が現状ないことが判明している

もちろんアップロード方式ではなくて、サーバ側でパスを見に行くようにすればいいのだが、そこは要検討

kiaikiai

安く済ませようとクラウド前提で考えていたけど、オンプレでやるという制約があるのをすっかり忘れていた(ガバクラ云々についてはまだ知らない)

ので、やっぱり1+3は同じサーバ上にあるのがよくて(=同じ設定ファイルやらパスやらを見に行ける)、ミニマムに始めるなら docker-compose のサービスごとでつくるのがよさそう

kiaikiai

overleaf 側で生ファイルを静的に保持してない問題

cf. https://github.com/overleaf/overleaf/wiki/Data-and-Backups#Folders-in-detail

  1. mongo / 2. redis / 3. fileSystem の3つで成り立ってるんだけど、これだけ見るとFSにいろいろファイルが置いてありそう

しかし実際には、"binary files of projects" しかないし、そのプロジェクトも名前ではなく id で管理されててつらい……つまり、

  1. .tex などのユーザ固有のファイルは、バイナリデータとして保持される
  2. プロジェクト名がフォルダ名と一致しない(≒フォルダから検索できない)

という問題が発生している


バイナリに変換している処理を見つけ出せば、その逆の処理とかバイナリを読み出す処理を見つけることでなんとかなりそう
他方、プロジェクト名で検索できないのは痛すぎる、どこかに id から逆引きした設定ファイルとかあるのだろうか……

kiaikiai

昨年末に謎のおっさんがいい感じのことを書いてくれている

https://github.com/overleaf/overleaf/issues/1300

リポジトリ見る限り何らかのヒントになりそうなスクリプトがありそうだ、これを深堀りしていい感じにどうにかできるだろうか……

kiaikiai

発想の再転換

もともとのモチベーションとしては「MS Office だと共同編集できなくてカス!!!!!!!!!!!!」というのが根源的だったんだけど、それは今後 TypeScript を導入して MS Office 365 Online でどうにかなるようだし(なるのか??)、実はそもそも共同編集できなくて困る機会は少ない(今回想定していたのも、Template.tex を有能マンが集まって編集できたら楽だよね、くらいのものだった)わけで、本質的に重要なのはテンプレートエンジンのほうじゃね?となってきた……

事実、Overleaf Community Edition でどうにかなるでしょと思っていた部分は痒いところに手が届かず、お金をかけて Server Pro Edition を使ったほうがいいような気がする(pro は試していないため気がするだけ…)
というか、まずはテンプレートエンジンをどうにか作って、そこから遡って考えるべき課題であると再考した

ので、一旦 Overleaf 側のアレコレはすべてふっ飛ばして、テンプレートエンジンを使ってエクセルないしCSVをデータソースとしたファイル生成を優先してやっていくこととする

kiaikiai

Open XML SDK for Office

結論、①テンプレートとしての word ファイル と ②データソースとしての excel ファイル があれば、その2つを受け取って一つあるいは複数の word ファイル(ないし PDF)を出力するシステムがほしい
そのためには、 Office ファイルを正しく弄ることができる仕組みが必要だろう
(UI/UXを担うフロント部分は React なり Next.js がどうにかできるだろう、保守が大変になるけども……)

ってことで、SDK でもあるかな~と思ったら提供されていたのだが……
https://learn.microsoft.com/ja-jp/office/open-xml/open-xml-sdk

nuget じゃん……ということは .NET というか C# というか……
https://www.nuget.org/packages/DocumentFormat.OpenXml

というわけで、フロント部分は js / バックエンドでの処理は C# を頑張ることになりそうだ……トホホ

kiaikiai

unzip して regexp して zip as docx すれば…?

Office ファイル系は無理くり unzip してやることで、中身を見ることができる

https://qiita.com/sky_y/items/2ab80adfb7a998e47239

https://www.antenna.co.jp/sbc/ooxml/i01-0001/i01-0001.html

このうち、word/documents.xml に該当する部分が、XML で構造化(マークアップ)されたテキスト本文であるようだ

…ってことは、これを 正規表現で置き換える、もとい mustache で捏ねるようにすればいいんじゃね?(もちろん {{}} の中身にマークアップあればそれが消えちゃうので、テンプレート作成者に留意が必要)

しかも、当然ながら zip して拡張子変えれば Office ファイルとして認識してくれるっぽい!
https://qiita.com/msattova/items/a862119cba0f81b035a2


実は SDK 使わずとも Python / js でなんとかなりそうな雰囲気が出てきた……かも?

kiaikiai

@zip.js/zip.js とかで zip / unzip できるようだ(ドキュメント読みづれえ……)

https://gildas-lormeau.github.io/zip.js/api/index.html
https://jsr.io/@zip-js/zip-js

現状の仕様としては以下の通り:

  1. docx + DataSource(xlsx / csv / json) をアップできる Form を設置
  2. それぞれ unzip して document.xml と 必要な入力項目を抽出
  3. サーバ側へ POST (Next.js App Router の route.ts を活用)
  4. xml は文字列として受け取り、入力データは JSON で受け取って Mustache で加工
  5. フロント側に いくつかの XML 文字列を返す
  6. 受け取った分だけ zip して docx ファイルを作成
  7. docx 群をさらに zip してダウンロード用のリンクを作成

本来的に言えば、ファイルをストレージに置くのが良いのだが、Vercel 上で完結するように作ると上記の仕様になる
本格的な導入を見据える場合には、別途アプリケーションサーバ以外にファイルを保存するストレージサーバとの連携を考慮するとよいだろう

kiaikiai

ブラウザ上で zip / unzip することのセキュリティ面での是非については別途議論を深堀りする必要がある。

現時点では問題なさそうだが、今後使用が変遷していくに連れていつの間にかセキュリティホールに変貌することもありうる。
また、Gemini 曰くXSS攻撃の温床となるかもしれないらしい。

他方、実現できれば非常に便利なツールとなることは想像に難くない。
今日明日で実装して、来週にはお披露目したい。