Skip to content

feat: Web UI で前処理フック・入力の読み込み・結果 Excel・入力項目を差し替えられるようにする - #26

Merged
ryaukwan merged 9 commits into
mainfrom
feat/web-ui-customization
Nov 5, 2026
Merged

ryaukwan merged 9 commits into
mainfrom
feat/web-ui-customization

Conversation

@ryaukwan

@ryaukwan ryaukwan commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

関連 Issue

Closes #24

変更概要

事業者が自治体向けにカスタマイズするときに、apps/ を改変(フォーク)せずに済むよう、リファレンスアプリ(Web UI・サーバ API)に差し替え口を設けました。

WebCustomization を継承したクラスを apps/ の外に実装し、環境変数 CHILMAI_WEB_CUSTOMIZATION=module:ClassName で指定すると、次の 4 か所を差し替えられます。

差し替え先 方法
前処理フック create_preprocessor(params) で BasePreprocessor を返す
入力ファイルの読み込み create_parser() で InputParser のサブクラスを返す(SHEET_NAME・HEADER_ROW の上書き、または read_table() のオーバーライド)
結果 Excel build_result_workbook()・result_filename()
画面の入力項目 fields に ParamField を定義し、値を params で受け取る

主な変更点:

  • コア(chilmai/)
    • InputParser に SHEET_NAME・HEADER_ROW と read_table() フックを追加しました。kind 引数で、申込者・保育所・組み合わせのファイルごとに読み方を変えられます。
    • MatchingService に parser 引数を追加しました。
    • エラーコード E109(CUSTOMIZATION_FAILED)を追加しました。
  • リファレンスアプリ(apps/)
    • 新規ファイル apps/api/customization.py に、差し替え口・入力値の検証・標準の結果 Excel をまとめました。
    • 画面の入力項目を追加しました。入力値を変えると、データ確認からやり直しになります。
    • サーバ API の /validate・/match で params(JSON 文字列)を受け付けるようにしました。
    • カスタマイズのメソッドが例外を出した場合は、E109 として返し、詳細はサーバのログに出力します。
    • 結果 Excel の数式セルは、すべて文字列として保存します。入力値が Excel で数式として実行されないようにするためです。
  • 配布 ZIP(apps/packager.py)
    • CHILMAI_WEB_CUSTOMIZATION を設定してビルドすると、次のように動きます。
      • 指定したモジュールを、hidden import として exe に組み込みます。
      • どのクラスを使うかの指定を、apps/api/bundled_customization.txt として同梱します。
    • 起動時に環境変数がなければ、同梱した指定を読みます。そのため自治体は ChilmAI.exe を起動するだけで使えます。
    • 指定に誤りがある場合は、exe を作る前にエラーで止まります。
  • サンプル・ドキュメント
    • サンプル examples/sample_web_customization.py を追加しました。年度・入所月の入力、備考列の追加、帳票タイトル行の読み飛ばし、施設別シートを実装しています。
    • docs/development/customization.md などを更新しました。

できなくなること:

  • 指定したクラスを読み込めない場合、アプリは起動時にエラーで終了します。標準の動作には切り替わりません。

できていないこと:

  • 配布 ZIP に組み込まれるのは Python のモジュールだけです。Excel のひな形などのデータファイルは同梱しません(ドキュメントに記載)。
  • THIRD-PARTY-NOTICES.txt とライセンスの検査の対象は、ChilmAI の依存ライブラリだけです。カスタマイズ用のライブラリは uv add で依存に加えれば対象になり、環境に入れただけのライブラリについては、事業者がライセンス表示を用意します(ドキュメントに記載)。

テスト結果

macOS で確認しました。

  • uv run python -m pytest test/ -m 'not (browser or binary)' -n auto:576 passed, 4 skipped
  • uv run python -m pytest test/chilmai/browser -m browser -n0:19 passed
    • 並列実行では、既存のテストを含めてポートが競合して失敗したため、並列なし(-n0)で実行しています。今回の変更とは関係ありません。
    • サンプルのカスタマイズを使うブラウザテストを追加しました。入力項目の初期値、入力値を変えたときのやり直し、ダウンロード時のファイル名を確認しています。
  • ruff check・ruff format --check・mkdocs build --strict:エラーなし
  • PyInstaller のビルド(macOS):サンプルのカスタマイズを指定してビルドし、環境変数なしで起動して次を確認しました。
    • 入力項目が表示されること
    • マッチングが実行できること
    • ファイル名が 選考結果_2027年度5月.xlsx になること
    • シートが result・施設別・meta になること
    • 結果の最後の列が備考列になること

チェックリスト

  • uv run python -m pytest test/ -m 'not (browser or binary)' -n auto がパスする
  • uv run python -m pytest test/chilmai/browser -m browser -n 1 がパスする(-n0 で実行。上記参照)
  • ドキュメントの更新が必要な場合は更新した(不要な場合はチェックして「対象外」と記載)
  • 実在する個人情報・自治体データ・本番ファイル・機密情報をコードやコメントに含めていない

その他

  • Windows でのビルドは未確認です。 実際に事業者がビルドする環境なので、マージ前に Windows で一度確認したいです。特に、カスタマイズ用のライブラリが DLL を持ち込んだ場合に、verify_runtime_dlls_are_accounted_for でビルドが止まらないかを確認したいです。
  • カスタマイズのモジュールはアプリと同じ権限で実行されます。信頼できるコードだけを指定する前提です(ドキュメントに記載)。
  • 環境変数を設定した場合は、配布 ZIP に同梱した指定より環境変数を優先します。

🤖 Generated with Claude Code

ryaukwan and others added 4 commits October 28, 2026 18:16
環境変数 CHILMAI_WEB_CUSTOMIZATION に module:ClassName 形式で
WebCustomization のサブクラスを指定すると、apps/ を改変せずに
次の 4 か所を差し替えられる。

- 前処理フック: create_preprocessor() で BasePreprocessor を返す
- 入力ファイルの読み込み: create_parser() で InputParser のサブクラスを返す
- 結果 Excel: build_result_workbook() / result_filename()
- 画面の入力項目: fields に ParamField を定義し、値を params で受け取る

コア側は MatchingService に parser 引数を追加し、InputParser に
SHEET_NAME・HEADER_ROW と read_table() フックを設けた。
サーバ API の /validate・/match は params(JSON 文字列)を受け付ける。

Closes #24

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- 空欄で送られた入力値を既定値で上書きしない(任意の select で「—」を選べるように)
- 数値の判定を半角の整数・小数に限定する(nan・inf・全角数字を弾く)
- type・checkbox の default・number の default の誤りを起動時に検出する
- カスタマイズのメソッドの想定外の例外を E109 として返し、500 エラーにしない
- 結果 Excel の数式の扱いについて、docstring・ドキュメントを実際の処理に合わせる
- transform_output の時点で結果列が末尾に ID・名の順で並ぶことを明記する

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CHILMAI_WEB_CUSTOMIZATION を設定して apps/packager.py でビルドすると、
指定したモジュールを hidden import として exe に組み込み、指定を
apps/api/bundled_customization.txt として同梱する。起動時に環境変数が
ない場合はこのファイルを読むため、自治体は exe を起動するだけで使える。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
build_customization_pyi_args() が呼ばれるたびに sys.path を書き換えて
いたため、テストで ROOT を差し替えると一時ディレクトリが残っていた。
python apps/packager.py で実行したときだけ必要なので __main__ に移す。

あわせて、カスタマイズ用のライブラリを uv add で依存に加えれば
ライセンスの一覧と検査の対象になることをドキュメントに追記する。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Coverage report

Click to see where and how coverage changed

FileStatementsMissingCoverageCoverage
(new stmts)
Lines missing
  apps/api
  customization.py 242
  main.py 768
  chilmai/generic
  error_codes.py
  parser.py
  service.py
Project Total  

This report was generated by python-coverage-comment-action

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

Stale in-flight responses can re-enable matching and expose results for outdated parameters, while unguarded customization calls bypass the intended E109 error handling.

3 open issues
What changed in this PR

Adds extension points to the reference Web UI and server API so municipalities can customize input handling, preprocessing, result workbooks, and form fields without modifying apps/, addressing #24.

Changes:

  • Introduces WebCustomization, parameter validation, configurable parsers, and customization error handling.
  • Connects custom fields and Excel output to the UI and bundles selected customizations into packaged executables.
  • Adds a sample customization, documentation, and API, packaging, and browser tests.
File Description
test/​chilmai/​browser/​test_customization_ui.py Tests custom fields, validation reset, and download filenames.
test/​apps/​test_packager.py Tests customization bundling and invalid specifications.
test/​apps/​test_customization.py Covers loading, parameters, parsers, output, and errors.
test/​apps/​test_api_contract.py Updates mocks for request-scoped services.
examples/​sample_web_customization.py Demonstrates all four customization points.
docs/​reference/​web-ui/​troubleshooting.md Documents E108 and E109 troubleshooting.
docs/​development/​customization.md Explains extension points and packaging.
docs/​api/​reference-app.md Documents API parameters and customization.
chilmai/​generic/​service.py Accepts injected parsers and passes file kind.
chilmai/​generic/​preprocessor.py Clarifies output-column order in the docstring.
chilmai/​generic/​parser.py Adds configurable sheets, headers, and read hooks.
chilmai/​generic/​error_codes.py Adds parameter and customization error codes.
apps/​packager.py Bundles customization modules and specifications.
apps/​api/​templates/​match_result.html Uses customized download filenames.
apps/​api/​templates/​index.html Renders custom fields and resets workflow state.
apps/​api/​static/​style.css Styles custom checkbox fields.
apps/​api/​static/​htmx-lite.js Disables matching after parameter changes.
apps/​api/​main.py Integrates custom services, parameters, and workbooks.
apps/​api/​customization.py Defines extension contracts, loading, and validation.

Review effort: Balanced

Comment on lines +236 to +239
form.querySelectorAll('[name^="param__"]').forEach(function (input) {
input.addEventListener('change', function () {
setMatchEnabled(form, false);
});
Comment thread apps/api/main.py Outdated
Comment on lines +98 to +99
preprocessor=_call_customization(customization.create_preprocessor, params),
parser=_call_customization(customization.create_parser),
Comment thread apps/api/main.py Outdated
try:
columns = InputParser.read_columns(
upload.file, _infer_file_format_from_filename(upload.filename)
columns = customization.create_parser().read_columns(
ryaukwan and others added 4 commits October 28, 2026 18:56
- データ確認の送信時の入力の世代を記録し、応答待ちの間にファイルや追加の入力項目が
  変わっていれば、応答が届いてもマッチングボタンを有効にしない
- カスタマイズが返した前処理フック・パーサーの公開メソッドの呼び出しにも例外の変換を
  適用し、transform_children() や read_table() の実行中の例外を 500 ではなく E109 として返す
  (ChilmError はそのまま返す。標準の InputParser・BasePreprocessor は対象外)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- データ確認・マッチングの応答待ちの間に入力が変わったら、変更前の入力の結果を表示せず、
  完了イベントの発火やボタンの再有効化もしない(入力変更による画面のリセットを戻さない)
- 項目名の設定画面の列名の自動検出でも、create_parser() とパーサーのメソッドの例外を
  E109 として表示する

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
表示先ごとに最新のリクエスト ID を記録し、最新のリクエストだけが応答の表示と
後処理(入力変更の案内・ボタンの有効化・通信エラーの表示)を行うようにする。

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ryaukwan ryaukwan self-assigned this Oct 29, 2026
@ryaukwan
ryaukwan requested a review from ciela October 29, 2026 00:18
- パーサー・前処理フックのメソッドを書き換える _guard_customized をやめ、
  エンドポイント側でカスタマイズ使用時だけ ValueError 以外の例外を E109 にする。
  同じインスタンスを使い回してもラッパーが重ならず、標準の処理や利用者のファイルに
  起因するエラーを E109 として表示しない
- 結果 Excel の戻り値を検証し、数式の無害化と保存の失敗も E109 にする
- Web UI で E108・E109 を「設定の問題」ではなく専用のセクションに表示する
- 既定値のない必須の select に「選択してください」を表示する
- checkbox に真偽値でない値が指定された場合は E108 にする
- 入力の変更で破棄したマッチングの応答で、ステップの「処理中」を戻す
- サンプルのパーサーで SHEET_NAME と標準の read_table を使う

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ryaukwan
ryaukwan merged commit 909bc94 into main Nov 5, 2026
11 checks passed
@ryaukwan
ryaukwan deleted the feat/web-ui-customization branch November 5, 2026 00:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Web UI で前処理フック・入力の読み込み・結果 Excel を差し替えられるようにする

2 participants