Skip to main content
Glama
html_generator.py8.13 kB
"""Jinja2テンプレートからHTML生成モジュール.""" import logging from datetime import datetime from pathlib import Path from typing import Any, Optional from jinja2 import Environment, FileSystemLoader, select_autoescape logger = logging.getLogger(__name__) class HTMLGenerator: """Jinja2テンプレートからHTML生成を行うクラス.""" def __init__(self): """初期化.""" template_dir = Path(__file__).parent / "templates" template_dir.mkdir(exist_ok=True) self.env = Environment(loader=FileSystemLoader(str(template_dir)), autoescape=select_autoescape(["html", "xml"])) def generate(self, template_name: str, context: dict, output_path: str) -> str: """HTMLレポート生成. Args: template_name: テンプレートファイル名 context: テンプレートに渡すコンテキスト output_path: 出力先HTMLファイルパス Returns: str: 生成されたHTMLファイルのパス Raises: FileNotFoundError: テンプレートが見つからない場合 """ try: template = self.env.get_template(template_name) except Exception as e: raise FileNotFoundError(f"テンプレートが見つかりません: {template_name}") from e # 基本コンテキストをマージ full_context = self._get_base_context() full_context.update(context) # HTMLレンダリング logger.info(f"HTMLレンダリング開始: {template_name}") html = template.render(**full_context) # ファイル出力 output_file = Path(output_path) output_file.parent.mkdir(parents=True, exist_ok=True) with open(output_file, "w", encoding="utf-8") as f: f.write(html) logger.info(f"HTMLレポート生成完了: {output_file}") return str(output_file) def update_ai_analysis(self, report_path: str, issues: list[dict], solutions: list[dict]) -> str: """既存HTMLレポートにAI分析結果を追加. Args: report_path: 既存のHTMLレポートパス issues: 課題リスト solutions: 解決策リスト Returns: str: 更新されたHTMLファイルのパス Raises: FileNotFoundError: レポートファイルが見つからない場合 """ report_file = Path(report_path) if not report_file.exists(): raise FileNotFoundError(f"レポートファイルが見つかりません: {report_path}") logger.info(f"AI分析結果の追加開始: {report_path}") # 既存HTMLを読み込む with open(report_file, "r", encoding="utf-8") as f: html_content = f.read() # issuesセクションのHTML生成 issues_html = self._generate_issues_html(issues) # solutionsセクションのHTML生成 solutions_html = self._generate_solutions_html(solutions) # HTMLを更新(ai_warningセクションを削除し、issues/solutionsを挿入) import re # 警告カードを削除 html_content = re.sub( r'<!-- AI_WARNING_START -->.*?<!-- AI_WARNING_END -->', '', html_content, flags=re.DOTALL ) # issuesセクションを更新 html_content = re.sub( r'<!-- AI_ISSUES_START -->.*?<!-- AI_ISSUES_END -->', f'<!-- AI_ISSUES_START -->\n{issues_html} <!-- AI_ISSUES_END -->', html_content, flags=re.DOTALL ) # solutionsセクション全体を更新 html_content = re.sub( r'<!-- AI_SOLUTIONS_START -->.*?<!-- AI_SOLUTIONS_END -->', f'<!-- AI_SOLUTIONS_START -->\n{solutions_html} <!-- AI_SOLUTIONS_END -->', html_content, flags=re.DOTALL ) # エグゼクティブサマリーの「検出課題」数値を更新 html_content = re.sub( r'(<span class="metric-value">)\d+(</span>\s*<span class="metric-label">検出課題</span>)', f'\\g<1>{len(issues)}\\g<2>', html_content ) # 更新されたHTMLを保存 with open(report_file, "w", encoding="utf-8") as f: f.write(html_content) logger.info(f"AI分析結果の追加完了: {report_file}") return str(report_file) def _generate_issues_html(self, issues: list[dict]) -> str: """課題セクションのHTML生成. Args: issues: 課題リスト Returns: str: 課題セクションのHTML """ if not issues: return "" html = '<ul class="issue-list">\n' for i, issue in enumerate(issues, 1): priority = issue.get("priority", "中").lower() title = issue.get("title", "") detail = issue.get("detail", "") impact = issue.get("impact", "") html += f''' <li class="issue-item priority-{priority}"> <div class="issue-title">{i}. {title}</div> <div class="issue-detail">{detail}</div> <div class="issue-meta"> <span class="issue-badge">優先度: {issue.get("priority", "中")}</span> ''' if impact: html += f' <span class="issue-badge">影響範囲: {impact}</span>\n' html += ' </div>\n' html += ' </li>\n' html += ' </ul>\n' return html def _generate_solutions_html(self, solutions: list[dict]) -> str: """解決策セクションのHTML生成. Args: solutions: 解決策リスト Returns: str: 解決策セクションのHTML """ if not solutions: return "" html = '' for i, solution in enumerate(solutions, 1): actions = solution.get("actions", []) difficulty = solution.get("difficulty", "") period = solution.get("period", "") expected_effect = solution.get("expected_effect", "") html += f''' <div class="solution-card"> <div class="solution-title">改善提案 {i}</div> ''' if actions: html += ' <div>\n' html += ' <strong>アクションプラン:</strong>\n' html += ' <ul class="action-list">\n' for action in actions: html += f' <li>{action}</li>\n' html += ' </ul>\n' html += ' </div>\n\n' html += ' <div class="issue-meta">\n' if difficulty: html += f' <span class="issue-badge">難易度: {difficulty}</span>\n' if period: html += f' <span class="issue-badge">期間: {period}</span>\n' html += ' </div>\n\n' if expected_effect: html += ' <div style="margin-top: 1rem; opacity: 0.9;">\n' html += f' <strong>期待効果:</strong> {expected_effect}\n' html += ' </div>\n' html += ' </div>\n\n' return html def _get_base_context(self) -> dict[str, Any]: """基本コンテキスト取得. Returns: dict: 基本コンテキスト """ return {"generated_at": datetime.now().strftime("%Y年%m月%d日 %H:%M:%S"), "version": "0.1.0"}

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/sinjorjob/survey-insight-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server