"""HTML レポート生成モジュール."""
import base64
from datetime import datetime
from pathlib import Path
import pandas
from jinja2 import Environment, FileSystemLoader
def encode_image_base64(image_path: str) -> str:
"""PNG 画像ファイルを Base64 エンコードした data URI に変換する.
HTML に画像をインライン埋め込みすることで、レポートを自己完結させる.
Args:
image_path: PNG 画像ファイルのパス.
Returns:
"data:image/png;base64,..." 形式の文字列.
ファイルが存在しない場合は空文字列を返す.
"""
path = Path(image_path)
if not path.exists():
return ""
with path.open("rb") as f:
encoded = base64.b64encode(f.read()).decode("utf-8")
return f"data:image/png;base64,{encoded}"
def build_report_context(
summary_csv_path: str,
spatial_csv_path: str,
figures_dir: str,
) -> dict:
"""CSV と図ファイルからテンプレート描画用のコンテキスト辞書を構築する.
画像は Base64 エンコードしてインライン埋め込みにするため、
レポート HTML が単独で完結する形になる.
Args:
summary_csv_path: 均一性サマリ CSV のパス(summary_uniformity.csv).
spatial_csv_path: 空間解析サマリ CSV のパス(summary_spatial.csv).
figures_dir: 各種図ファイルが格納されたディレクトリのパス.
Returns:
Jinja2 テンプレートに渡すコンテキスト辞書.キーは:
"generated_at", "summary_rows", "spatial_rows", "image_details".
Raises:
FileNotFoundError: CSV ファイルが存在しない場合.
"""
summary_path = Path(summary_csv_path)
if not summary_path.exists():
raise FileNotFoundError(
f"均一性サマリ CSV が見つかりません: {summary_csv_path}"
)
spatial_path = Path(spatial_csv_path)
if not spatial_path.exists():
raise FileNotFoundError(
f"空間解析サマリ CSV が見つかりません: {spatial_csv_path}"
)
summary_df = pandas.read_csv(summary_path)
spatial_df = pandas.read_csv(spatial_path)
figures_path = Path(figures_dir)
# 画像別詳細: サマリに登場する image_name ごとに図を収集する
image_details = []
for image_name in summary_df["image_name"].tolist():
detail = {
"name": image_name,
"luminance_map": encode_image_base64(
str(figures_path / f"{image_name}_luminance_map.png")
),
"histogram": encode_image_base64(
str(figures_path / f"{image_name}_histogram.png")
),
"radial_profile": encode_image_base64(
str(figures_path / f"{image_name}_radial_profile.png")
),
"zone_map": encode_image_base64(
str(figures_path / f"{image_name}_zone_map.png")
),
}
image_details.append(detail)
return {
"generated_at": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
"summary_rows": summary_df.to_dict(orient="records"),
"spatial_rows": spatial_df.to_dict(orient="records"),
"image_details": image_details,
}
def render_html_report(context: dict, template_path: str, output_path: str) -> None:
"""Jinja2 テンプレートにコンテキストを適用して HTML レポートを生成する.
Args:
context: テンプレートに渡すコンテキスト辞書(build_report_context の戻り値).
template_path: Jinja2 テンプレートファイルのパス.
output_path: 出力先 HTML ファイルのパス.
"""
template_file = Path(template_path)
env = Environment(
loader=FileSystemLoader(str(template_file.parent)),
autoescape=True,
)
template = env.get_template(template_file.name)
html = template.render(**context)
out_path = Path(output_path)
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(html, encoding="utf-8")
print(f"HTML レポートを保存しました: {output_path}")