Redmine Advent Calendar 2024 の 15日用の記事です。
初めに
※ 本記事は私の主観的な思いが多く含まれています。
読みにくいですがご容赦いただけますと幸いです。
社内Wikiは便利ですよね!
最新情報を共有する際はURLを1つ送るだけで済みますし、編集履歴や差分も簡単に確認できます。また、オフラインで参照したいときにはPDFエクスポート機能も用意されています。
私はオンプレのRedmineに搭載してるwikiを使って、残タスク(チケット)と紐付けながら共有情報を残します。
ところで、私事ではありますが、誰かにドキュメントを提出した時、皆さんもこんな依頼を受けたことはありませんか?
PJリーダー:「いや、PDFじゃなくてWordで欲しいんだけど。」
…え?いや…提出物にPDFで十分じゃないですか…ていうか、社内の人ならwikiページ見たらそれで良いじゃな…
しかし、現実は甘くありません。
私は権力には逆らえず、RedmineのWikiに書いた情報をWordやExcel形式に変換する作業を頻繁に行う羽目になりました。
情報の中身は変わらず、ただ体裁を整えるだけの作業。これに時間を取られるのは非常に苦痛でした。
ただ、この経験がきっかけで、RedmineのWikiからWordファイルを自動的に変換するツールを開発するに至りました。
本記事では、そのツールの動作プロセス紹介や、作ってみて得られた知見について紹介します。
WikiからWordへの手動変換の苦悩と自動変換への動機
RedmineのWikiには、「HTML」や「PDF」でエクスポートできる機能があります。この機能を使えば、プロジェクト内にある複数のWikiページをワンボタンで出力することが可能です。
参考URL: RedmineでWikiページをすべてエクスポートする方法
「PDFからWordに変換するだけなら、WebアプリやPandocを使えば簡単にできそうだな」と、思ってました。
しかし、Word提出とは単にPDFをWordに変換するだけで済む話では無かったのです。
私の想定と違い「WORDで欲しい」には隠れた要求があります。
それは「再編集性」や社内の人が定義したフォーマットに従った「整った体裁」です。これらの要求は、私にとっては客先提出資料じゃないんだから、できれば程度の要求と思っていました。ですが、後にPJリーダーから叱られて当たり前品質であることがわかりました。
これらの内部要求が外部要求化した事で、現行Redmineの標準Wikiエクスポート機能には以下の問題点が挙げられます。
- 標準Wikiエクスポート機能の問題点
- 出力順序の問題
 WikiページがUnicode順に出力されるため、Wordに変換した後でページ順を手動調整する必要がある。
- ページ選択の制限
 エクスポート対象のページを選べないため、後で不要なページを削除する手間が発生する。
 ※Wikiの設計段階で不要なページを作らない運用も可能だが、それではWiki本来の自由度が損なわれる。
- 体裁の問題
 Word提出時に必要な表紙や目次などを、変換後に追加しなければならない。
 
- 出力順序の問題
- PDFエクスポートからWord変換の問題点
- 体裁の崩れ
 PDFをWordに変換した際、PDFの体裁と異なる箇所が多数発生する。
 変換ツールによる差異もある。
- 不要情報の出力
 PDFにWikiの添付ファイルリストなど余分な情報が含まれるが後で加工が必要。
 
- 体裁の崩れ
- HTMLエクスポートからWord変換の問題点
- 目次の不要化
 ページ内目次はWordには不要なため、削除作業が必要になる。
- 表の枠線表示の不具合
 表の枠が見えないスタイルの問題が発生する。
- 画像表示の問題
 サーバの画像を表示しようとしてオフラインで表示できない
- ページ区切りの欠如
 Wikiページ単位でWordのページ改行が自動設定されないため、適宜手動で編集が必要。
 
- 目次の不要化
と、このように、現行の機能を使った場合、Word変換後の問題があり、手直しがあまりにも多過ぎます。
急ぎの依頼時には、必要な情報を手動でWordにコピペして対応しましたが、20ページを超えるころには操作ミスが増え、限界を感じます。1回限りの作業なら我慢はできても「これが今後も繰り返されるのか」と絶望していました。
絶望が顔に現れていた頃に周りからは「初めからWordで資料を作れば良いのに」と、度々アドバイスを貰いました。
ですが私の属したPJでは、おじいちゃんを相手にするので、ファイルの内容や更新履歴を何故か書き手が管理することになります。
いつのどの書類のどこに例の情報があるのか?と、関係者に問われ続けて一生書き手から手離れしません。それも仕事だと飲み込もうとはしたのですが、私自身の仕事で残業時間が増えてゆくので無理になりました。
※ イメージ図として適切な参考URLを下記に貼っておきます
1匹いたら20匹いると思え #現場猫 pic.twitter.com/yqilDQgBbE
— からあげのるつぼ (@karaage_rutsubo) September 14, 2022
持論ですが、蓄積してゆく情報とは読み手も十分に関わって、内容のフィードバックをして情報をもっと育てるべき。
完璧なもので無くても内容を読んで感想が欲しいからこそ、簡便な共有方法は重要であり、共有を妨げる1ファイルによるナレッジの蓄積は避けたいのです。
こうした苦悩から、私はWikiで情報を蓄積したい。という思いと、PJリーダはWordで提出しないと読んでやらない。
を、両立するために、自動的にWikiをWordに変換する仕組みを作る必要性を強く感じるようになりました。
自動化の目標と環境
- 
自動化の目標 
 RedmineのWikiに記載した情報を、関係者間で使用されるフォーマットに従った1つのWordファイルにまとめ、そのまま提出できる形にすることを目指します。達成しなくとも、手動での変換作業の負担を減らし、再現性のある形で効率化することが主な目的です。
- 
動作環境 
 ※ 全体的に古い環境だし、Markdownじゃないです…。- Redmineバージョン: 4.2.1 stable
 (利用するプラグイン: Redmine draw.io plugin)
- マークアップ言語: textile
- Wordバージョン: 2016
- 開発環境: Windows 10 64bit、Python 3.11(各種ライブラリ)
 
- Redmineバージョン: 4.2.1 stable
自動化ツールの動作解説
ここからは、作ったツールの中身に触れますが、ソフト要件を中心に一部設計の説明します。
TODO: ここに動作している動画を載せたい…
自動化ツールの全体概要
本ツールは、以下のような機能ブロックで構成されています。
簡単に言えば「WikiからHTMLをエクスポートして、編集・加工して、WordでHTMLを開いてWord形式に保存しなおす」
という手動の流れを自動化しただけです。
Pandocとかの変換ツールと違うのは、WordがHTML保存可能なので、Wordの親和性を維持したHTMLを流用して変換できる事です。
各機能の説明
ダウンローダ
最新情報はRedmineのWikiにあるとして、一先ずAPIを使ってクライアントPCに最新情報をダウンロードします。
PythonでRedmineにアクセスする際には、python-redmine というライブラリが便利です。このライブラリを使用することで、簡単にRedmineにアクセスし、データを取得できます。
なお、Redmineサーバー側の設定が必要となります。以下の記事を参考に設定しました。
参考記事:Redmine REST APIを有効化する方法
ダウンローダは、python-redmine を使用して、対象プロジェクトのWikiページ単位の情報をとしてダウンロードします。
(これらはrawデータとして後で再編集に使います)
 また、Redmine標準のエクスポート機能を使い、Wiki全体のデータをHTML形式でダウンロードします。
また、Redmine標準のエクスポート機能を使い、Wiki全体のデータをHTML形式でダウンロードします。
しかし、このエクスポート機能のHTML形式はオフラインで画像表示できない問題がありました。そのため、ダウンロード後に必要な加工処理を幾つか行います。
なお、HTMLの加工はBeautiful Soup 4というライブラリを使うとHTMLのタグ単位で加工ができるので楽です。
- ダウンロードしたローカルPC上の添付ファイルと新たにリンクを結び直す
- 一部の見えにくいスタイルを別のスタイルに変更する
- 次工程で利用するエクスポート機能に向けて、Wikiページのタイトルリンクにクラス分けする
これらの加工により、RedmineのHTMLエクスポートデータはオフライン利用が可能となります。WebクローラーツールでWebページの完全保存をした後みたいな感じです。
この時点で、Redmineの標準エクスポート機能の不満は解消されたと言えます…!
私個人にはこれ位で十分なのですが、以降はWord変換に向けて作業は続きます。
Wordコンバータに向けた手作業
WordにエクスポートするWikiページの要不要、順番をタイトルから検討し、目次情報を作ります。
固定したタイトルをあいうえお順に定義するとかしても良いですが、ここは手作業とします。
Wordで文章を作ったとしても、目次から何の情報をどのような順番で見せたいかを決めるのは手動です。同じことです。
ただ、一度定義した目次情報は使いまわしたく、タイトルの要不要と順番、階層情報はjsonファイル保存しておきます。同じプロジェクトのWord変換時は流用してゆきます。
このデスクトップアプリは、規格物のjsonファイル加工ですのでChatGPT先生にお任せしました。
雑な指示でも作ってくれて、大変御世話になっております…!

Wordコンバータ~前加工~
手作業で行った目次情報に沿うように、Wikiページ単位の情報の取り出して順番を変えながら、必要な情報のみ集めた一つのhtmlファイルを作ります。
この他にも
- ページ内の目次を消す
- ページの最後にWord改ページ構文(HTML)を挿入する
- ページ内の見出しレベルを3以上に調整して、Word目次への表示がされないようにする
- Word2016向けにsvg → emf変換
- etc.....
色々加工します。中でも特徴的な加工は、エクスポート対象のページにサーバのWikiと連携できるリンクを追加したことです。
Word上では以下のような表示になります。

このリンクは、Wikiを原本にしてWordはあるタイミングのスナップショットであるという従属関係を読み手に伝えられます。
さらに、Wiki最新との差分表示もリンクになることで、読み手自身が古い情報と最新とのギャップをグラフィカルに気が付けます。
参考URL:RedmineのWikiの活用方法と便利な機能紹介
一方で書き手は読み手にいつの情報を渡したのかは忘れて、Wikiに最新情報を積み重ねるだけで良くなります。
私がそうなのですが、メンテされてない古いドキュメントが後世に渡り、それが妙に関係者から信頼されてる書類だと、現物とのズレに非常に混乱します。最終的に当時の文責について推論したり、正しい情報の証明コストが発生します。
ドキュメントとはいつまでも完成しないものではあります。でも、せめて後世の読み手は情報の鮮度を感じ取って注意して読んで貰えたらと願うので、私は日付けと最新差分リンクを埋め込んだもので提出します。
もちろん古い情報までを公開して、最新情報は秘匿にしたい場合もあります。でも、RedmineのWikiはプロジェクトメンバのみ閲覧可能にすれば、万が一、資料が客先に渡っても必要以上の情報を渡さずに済みます。
※ていうか、客に内部資料を渡さないようにしましょう…
Wordコンバータ~変換~
必要な情報だけになったhtmlは、後は変換するだけです。
でも、Word固有のフレームやWordの目次を後で追加する手間を解決するために、先にWord側でdocmファイルを用意します。
提出形態に合わせた表紙やヘッダーフッター、スタイル等など、手作業でテンプレ docxを作ります。
その後、Pythonからpywin32ライブラリを使って、docm → html変換したテンプレHTMLを加工用に作成しておきます。
参考URL:Python(pywin32)でWordを操作する[2] - Wordを起動/終了する
なお、テンプレHTMLの元になるテンプレdocxにはオリジナルの文字列をフィールドコードを埋め込む仕様にしています。
これはstart~endをWiki挿入範囲、他の箇所はテンプレdocxの情報のまま使う区切り文字の役目です。
なんだかWikiの範囲を差し替えればもう完成しそうですが、Wordはhtmlと違って印刷用紙サイズに縛られがちなアプリです。
全てにおいて用紙サイズに配慮する必要であり、これはWordにとってネイティブ言語(?)であるVBAをpywin32経由で実行しました。
でも、VBAの遅さを舐めてました…なるべくhtmlの前加工で行ったほうが良いと反省しています。
VBAでは
- png/svgは画像サイズがA4を越えているかチェックして、サイズを小さくします
- 画像がリンク先に存在しているのでdocファイルに埋め込みます
- スタイルの再適用をします
- 目次の更新を行います
最後、不要一時ファイルの削除や、ファイルの移動を行ってファイルを開きます。
以上で、WikiからWord自動変換の行程は終了です。
作ってみて思ったこと
いざ中身を見ると、若干ズレはあり体裁上の問題はまだ残っています。
でも、私のプロジェクトをWord化したところ100ページくらいの内容になったので、相互参照を作る手間が無くなっただけでも自動化ツールはありがたいです。何より変換中は目をつぶって休憩できるのがいい感じです!!
なお、Wordの1ファイルにまとめたことで、文章校正ツールが文字のゆらぎを見つけてくれます。Redmineで単ページの文章を作ってたら気が付かない点でして、一つのWordにまとめたことでの嬉しい誤算でした。
再編集はしやすい?
主な編集手段は3択くらいでしょうか。
- Wordで直接編集する
 WordはWYSIWYG(What You See Is What You Get)を体現しているため、体裁に関する編集は非常に直感的で簡単にできます。
 一人で提出物の外見やフォーマット調整したい場合、この方法が最適です。ただし、Wikiと連動しなくなるので中身の編集はお勧めしません。
- RedmineでWiki編集し、再度ダウンローダで変換する
 中身の更新が必要な場合、Redmine上のWikiを直接編集して、再びダウンロードしてWord化します。複数人で分担して最新情報を記入できますので、Wiki編集のメリットが享受できます。
- Wikiページのrawデータをテキストエディタで編集してアップロードする
 中身の編集量が多い場合には、Wikiのrawデータをローカル環境で編集し、アップローダでサーバに反映させます。
 ダウンローダはWikiのテキスト情報や添付ファイルをローカルに取得しているため、エクスプローラーから目的のファイルを直接好きなエディタで編集することが可能です。
この中で、3番が特徴的で気に入ってます。
ダウンロード処理によって、ローカルに保存されたWikiのテキスト情報やSVGファイルはテキスト形式で保存されています。これらは一般的なテキストエディタならば、ファイルの中身を開くこと無くGrep置換が可能です。
ちょっとした文字の揺らぎはGrep置換により、文字列を一括で検索・置換できます。
更に、SVG内のテキストも置換できるので、画像内の文字列も検索対象にして修正が可能になります。画像の修正も同時にできるのが便利です。
参考URL VSCodeで複数ファイル内のテキストの一括置換する方法、等の忘備録
RedmineのWikiもそうですが、Web媒体の情報をフロント側の編集画面で一括編集する方法は需要が少なすぎて提供されないと思います。
情報を増やす程、修正する時にはWiki内の検索結果から力ずくで修正するしかなく、地味にストレスの溜まる行為でした。
この点は、ローカルgrep置換によって編集機能が拡張されたと言ってよいと思います。
※ 今回アップローダ機能は説明を省略しますが、変更ファイルだけを抽出して、API越しにテキストと添付ファイルをアップする仕様で作りました。
APIのアップロード機能であるため、一括で修正した情報もWiki側の編集履歴として残りますので安心ですね。
ダウンロードした各ファイルを、ローカル内でバージョン管理すると、一括編集の変化点を確認できます。
Redmine側と2重管理になるので、アップロード前の一時的な変化点管理に使うと良いです。
画像の再編集について
画像ファイルのSVGの場合、diagrams.net(旧名 draw.io) を利用してGUIで編集すると楽な再編集ができるのでお勧めです。
これは、ローカルアプリ上でもRedmine上のプラグイン経由でも、同じ操作感で気が付いた時にサッと編集できるからです。
- ローカル環境
 diagrams.netのデスクトップ版(draw.io Desktop)を使用
 VScode + Draw.io Integration を使用
- Redmine環境
 Redmineではdraw.ioプラグインを使用して表示や編集をサポート
ちなみに、Mermaidによる作図も良いのですが、図形の位置関係に狙いがあると作図が難しかったり、Word作図がメインの人が馴染めません。
なので、draw.ioが多くのシーンで使い易いと思っています。
余談ですが、RedmineにおけるSVG表示は標準ではできませんでした。
参考URL: Feature #2047: Add SVG support, like images
ですが、draw.ioプラグインを使用することで、Webでのsvg編集と同時に、svgの表示がマクロ構文で可能になります。
参考URL: Redmineでdraw.ioプラグインを使用する
有料のWikiはMermaidかPluntUMLのダイアグラムツールをサポートしていることが多い印象です。draw.ioみたいなGUI作図ツールとWikiの組み合わせは無いので、エンジニア界隈以外は辛く無いんですかね?(詳しく知りませんが)。
今後は
環境を新しくして、Microsoft365とRedmineの最新バージョンとCommonMarkdownの組み合わせで動くように作り、なるべく一般化したいです。
そこまでしたら、GitHubにソースコードを公開しようかと。
でも、クライアント側のpythonで何とかしようとしてますが、サーバー側で解消する方法がより正攻法で需要があるとは思ってます。いやdoxygenみたいにCIが使えるからこのままでも良いんかな?(どうしたら良いか方向性が見えません…)
最後に
PJリーダや、Wordの事が嫌いになりそうだったのですが、怒りや苦しみの力を開発にぶつけることができ、技術で課題を解決しました(?解決しつつあります)。
怒りから先に手が出ずに、きちんと要求を受け取って技術で対応できるとは…
私にも健全なエンジニア魂があったことに自分でも驚いてます。
なんだがこの先もエンジニアやってゆける気がしてきましたよ!!
Qiita初投稿も含めて全て良い経験でした。
また、アウトプット出来るネタ(次はPJリーダとのぶつかりあいで得られた知見とか?)があればやってみます。
ありがとうございました...!

