213 lines
8.2 KiB
Python
213 lines
8.2 KiB
Python
"""
|
|
Markdown renderer for report generation.
|
|
"""
|
|
|
|
from .rendererBaseTemplate import BaseRenderer
|
|
from typing import Dict, Any, Tuple, List
|
|
|
|
class RendererMarkdown(BaseRenderer):
|
|
"""Renders content to Markdown format with format-specific extraction."""
|
|
|
|
@classmethod
|
|
def get_supported_formats(cls) -> List[str]:
|
|
"""Return supported Markdown formats."""
|
|
return ['md', 'markdown']
|
|
|
|
@classmethod
|
|
def get_format_aliases(cls) -> List[str]:
|
|
"""Return format aliases."""
|
|
return ['mdown', 'mkd']
|
|
|
|
@classmethod
|
|
def get_priority(cls) -> int:
|
|
"""Return priority for markdown renderer."""
|
|
return 95
|
|
|
|
async def render(self, extracted_content: Dict[str, Any], title: str, user_prompt: str = None, ai_service=None) -> Tuple[str, str]:
|
|
"""Render extracted JSON content to Markdown format."""
|
|
try:
|
|
# Generate markdown from JSON structure
|
|
markdown_content = self._generate_markdown_from_json(extracted_content, title)
|
|
|
|
return markdown_content, "text/markdown"
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error rendering markdown: {str(e)}")
|
|
# Return minimal markdown fallback
|
|
return f"# {title}\n\nError rendering report: {str(e)}", "text/markdown"
|
|
|
|
def _generate_markdown_from_json(self, json_content: Dict[str, Any], title: str) -> str:
|
|
"""Generate markdown content from structured JSON document."""
|
|
try:
|
|
# Validate JSON structure
|
|
if not isinstance(json_content, dict):
|
|
raise ValueError("JSON content must be a dictionary")
|
|
|
|
if "sections" not in json_content:
|
|
raise ValueError("JSON content must contain 'sections' field")
|
|
|
|
# Use title from JSON metadata if available, otherwise use provided title
|
|
document_title = json_content.get("metadata", {}).get("title", title)
|
|
|
|
# Build markdown content
|
|
markdown_parts = []
|
|
|
|
# Document title
|
|
markdown_parts.append(f"# {document_title}")
|
|
markdown_parts.append("")
|
|
|
|
# Process each section
|
|
sections = json_content.get("sections", [])
|
|
for section in sections:
|
|
section_markdown = self._render_json_section(section)
|
|
if section_markdown:
|
|
markdown_parts.append(section_markdown)
|
|
markdown_parts.append("") # Add spacing between sections
|
|
|
|
# Add generation info
|
|
markdown_parts.append("---")
|
|
markdown_parts.append(f"*Generated: {self._format_timestamp()}*")
|
|
|
|
return '\n'.join(markdown_parts)
|
|
|
|
except Exception as e:
|
|
self.logger.error(f"Error generating markdown from JSON: {str(e)}")
|
|
raise Exception(f"Markdown generation failed: {str(e)}")
|
|
|
|
def _render_json_section(self, section: Dict[str, Any]) -> str:
|
|
"""Render a single JSON section to markdown."""
|
|
try:
|
|
section_type = self._get_section_type(section)
|
|
section_data = self._get_section_data(section)
|
|
|
|
if section_type == "table":
|
|
return self._render_json_table(section_data)
|
|
elif section_type == "bullet_list":
|
|
return self._render_json_bullet_list(section_data)
|
|
elif section_type == "heading":
|
|
return self._render_json_heading(section_data)
|
|
elif section_type == "paragraph":
|
|
return self._render_json_paragraph(section_data)
|
|
elif section_type == "code_block":
|
|
return self._render_json_code_block(section_data)
|
|
elif section_type == "image":
|
|
return self._render_json_image(section_data)
|
|
else:
|
|
# Fallback to paragraph for unknown types
|
|
return self._render_json_paragraph(section_data)
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Error rendering section {self._get_section_id(section)}: {str(e)}")
|
|
return f"*[Error rendering section: {str(e)}]*"
|
|
|
|
def _render_json_table(self, table_data: Dict[str, Any]) -> str:
|
|
"""Render a JSON table to markdown."""
|
|
try:
|
|
headers = table_data.get("headers", [])
|
|
rows = table_data.get("rows", [])
|
|
|
|
if not headers or not rows:
|
|
return ""
|
|
|
|
markdown_parts = []
|
|
|
|
# Create table header
|
|
header_line = " | ".join(str(header) for header in headers)
|
|
markdown_parts.append(header_line)
|
|
|
|
# Add separator line
|
|
separator_line = " | ".join("---" for _ in headers)
|
|
markdown_parts.append(separator_line)
|
|
|
|
# Add data rows
|
|
for row in rows:
|
|
row_line = " | ".join(str(cell_data) for cell_data in row)
|
|
markdown_parts.append(row_line)
|
|
|
|
return '\n'.join(markdown_parts)
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Error rendering table: {str(e)}")
|
|
return ""
|
|
|
|
def _render_json_bullet_list(self, list_data: Dict[str, Any]) -> str:
|
|
"""Render a JSON bullet list to markdown."""
|
|
try:
|
|
items = list_data.get("items", [])
|
|
|
|
if not items:
|
|
return ""
|
|
|
|
markdown_parts = []
|
|
for item in items:
|
|
if isinstance(item, str):
|
|
markdown_parts.append(f"- {item}")
|
|
elif isinstance(item, dict) and "text" in item:
|
|
markdown_parts.append(f"- {item['text']}")
|
|
|
|
return '\n'.join(markdown_parts)
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Error rendering bullet list: {str(e)}")
|
|
return ""
|
|
|
|
def _render_json_heading(self, heading_data: Dict[str, Any]) -> str:
|
|
"""Render a JSON heading to markdown."""
|
|
try:
|
|
level = heading_data.get("level", 1)
|
|
text = heading_data.get("text", "")
|
|
|
|
if text:
|
|
level = max(1, min(6, level))
|
|
return f"{'#' * level} {text}"
|
|
|
|
return ""
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Error rendering heading: {str(e)}")
|
|
return ""
|
|
|
|
def _render_json_paragraph(self, paragraph_data: Dict[str, Any]) -> str:
|
|
"""Render a JSON paragraph to markdown."""
|
|
try:
|
|
text = paragraph_data.get("text", "")
|
|
return text if text else ""
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Error rendering paragraph: {str(e)}")
|
|
return ""
|
|
|
|
def _render_json_code_block(self, code_data: Dict[str, Any]) -> str:
|
|
"""Render a JSON code block to markdown."""
|
|
try:
|
|
code = code_data.get("code", "")
|
|
language = code_data.get("language", "")
|
|
|
|
if code:
|
|
if language:
|
|
return f"```{language}\n{code}\n```"
|
|
else:
|
|
return f"```\n{code}\n```"
|
|
|
|
return ""
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Error rendering code block: {str(e)}")
|
|
return ""
|
|
|
|
def _render_json_image(self, image_data: Dict[str, Any]) -> str:
|
|
"""Render a JSON image to markdown."""
|
|
try:
|
|
alt_text = image_data.get("altText", "Image")
|
|
base64_data = image_data.get("base64Data", "")
|
|
|
|
if base64_data:
|
|
# For base64 images, we can't embed them directly in markdown
|
|
# So we'll use a placeholder with the alt text
|
|
return f""
|
|
else:
|
|
return f""
|
|
|
|
except Exception as e:
|
|
self.logger.warning(f"Error rendering image: {str(e)}")
|
|
return f""
|