応用①:フォーム仕様書の Markdown 自動生成
抽出した InputEntity リストを Markdown のテーブルとして出力すれば、 そのまま仕様書・設計書のドラフトとして使える。
from extractor import extract_inputs
from extractor.models import InputEntity
from pathlib import Path
def generate_markdown_spec(entities: list[InputEntity], title: str = "フォーム仕様書") -> str:
"""InputEntity リストを Markdown テーブルに変換する"""
lines = [f"# {title}", "", "| # | 表示名 | type | name | 必須 | 最大文字 | 選択肢 |",
"|---|--------|------|------|------|----------|--------|"]
for i, e in enumerate(entities, start=1):
label = e.label or "(なし)"
options = " / ".join(e.options[:5]) if e.options else "-"
if len(e.options) > 5:
options += " ..."
lines.append(
f"| {i} | {label} | `{e.type}` | `{e.name or '-'}` "
f"| {'✅' if e.required else '-'} "
f"| {e.maxlength or '-'} "
f"| {options} |"
)
return "\n".join(lines)
if __name__ == "__main__":
url = "https://httpbin.org/forms/post"
entities = extract_inputs(url)
md = generate_markdown_spec(entities, title=f"フォーム仕様書 — {url}")
out = Path("output/form_spec.md")
out.parent.mkdir(exist_ok=True)
out.write_text(md, encoding="utf-8")
print(md)
出力 Markdown(抜粋)
# フォーム仕様書 — https://httpbin.org/forms/post | # | 表示名 | type | name | 必須 | 最大文字 | 選択肢 | |---|--------|------|------|------|----------|--------| | 1 | Customer name | `text` | `custname` | - | - | - | | 2 | Telephone | `tel` | `custtel` | - | - | - | | 3 | E-mail address | `email` | `custemail` | - | - | - | | 4 | Pizza Size | `select` | `size` | - | - | Small / Medium / Large | | 5 | Any comments? | `textarea` | `comments` | - | - | - |
応用②:テストケースインプット生成
required / maxlength / type 情報からテスト設計でよく使う入力値候補を自動生成できる。
from extractor.models import InputEntity
def generate_test_inputs(entity: InputEntity) -> list[dict]:
"""
1 つの InputEntity から境界値・等価クラスのテスト入力値を生成する。
Returns:
{"case": "説明", "value": 入力値, "expected": "期待結果"} のリスト
"""
cases = []
# 必須チェック
if entity.required:
cases.append({"case": "必須: 未入力", "value": "", "expected": "バリデーションエラー"})
cases.append({"case": "必須: 入力あり(正常)", "value": "テスト値", "expected": "OK"})
# 最大文字数チェック
if entity.maxlength:
max_len = int(entity.maxlength)
cases.append({"case": f"最大文字: 上限ちょうど ({max_len}文字)",
"value": "A" * max_len, "expected": "OK"})
cases.append({"case": f"最大文字: 上限超過 ({max_len + 1}文字)",
"value": "A" * (max_len + 1), "expected": "バリデーションエラー"})
# type 別テスト値
if entity.type == "email":
cases.append({"case": "email: @なし", "value": "invalid", "expected": "バリデーションエラー"})
cases.append({"case": "email: 正常形式", "value": "test@example.com", "expected": "OK"})
elif entity.type == "number":
if entity.min:
cases.append({"case": f"number: 下限未満 ({entity.min} - 1)",
"value": str(int(entity.min) - 1), "expected": "バリデーションエラー"})
if entity.max:
cases.append({"case": f"number: 上限超過 ({entity.max} + 1)",
"value": str(int(entity.max) + 1), "expected": "バリデーションエラー"})
elif entity.type in {"select"}:
for opt in entity.options:
cases.append({"case": f"select: 正常値 '{opt}'", "value": opt, "expected": "OK"})
return cases
# 使用例
from extractor import extract_inputs
entities = extract_inputs("https://httpbin.org/forms/post")
for e in entities:
test_cases = generate_test_inputs(e)
if test_cases:
print(f"\n--- {e.label or e.name} ({e.type}) ---")
for tc in test_cases:
print(f" [{tc['case']}] value={tc['value']!r:20} → {tc['expected']}")
発展:DB カラム候補の推定
InputEntity の属性から DB カラムの型・制約を推定するヒューリスティックを実装できる。
TYPE_MAP = {
"text": "VARCHAR",
"email": "VARCHAR",
"tel": "VARCHAR",
"password": "VARCHAR",
"number": "INTEGER",
"date": "DATE",
"textarea": "TEXT",
"checkbox": "BOOLEAN",
"radio": "VARCHAR",
"select": "VARCHAR",
"hidden": "VARCHAR",
}
def to_column_def(e: InputEntity) -> dict:
sql_type = TYPE_MAP.get(e.type, "VARCHAR")
if sql_type == "VARCHAR" and e.maxlength:
sql_type = f"VARCHAR({e.maxlength})"
return {
"column_name": e.name or "(不明)",
"sql_type": sql_type,
"not_null": e.required,
"comment": e.label or e.placeholder or "",
}
📌 これはあくまで「出発点」
DB 設計はドメイン知識が必要な作業だ。自動推定した結果を無批判に採用するのではなく、設計者がレビューして調整する前提で使うこと。