Skip to main content
Glama
format_exporter.py27.2 kB
""" Multi-format documentation exporter with theme support. This module provides comprehensive export capabilities for documentation in various formats including HTML, PDF, Word, Confluence, and more. """ import json import logging import tempfile from typing import Dict, Any, List, Optional, Union from pathlib import Path from dataclasses import dataclass from enum import Enum logger = logging.getLogger(__name__) class ExportFormat(Enum): """Supported export formats.""" HTML = "html" PDF = "pdf" DOCX = "docx" MARKDOWN = "markdown" CONFLUENCE = "confluence" NOTION = "notion" JSON = "json" EPUB = "epub" class Theme(Enum): """Available themes.""" DEFAULT = "default" DARK = "dark" MINIMAL = "minimal" CORPORATE = "corporate" GITHUB = "github" MATERIAL = "material" @dataclass class ExportOptions: """Export configuration options.""" format: ExportFormat theme: Theme = Theme.DEFAULT include_toc: bool = True include_diagrams: bool = True include_code_highlighting: bool = True include_search: bool = True custom_css: Optional[str] = None output_path: Optional[str] = None title: Optional[str] = None class DocumentationExporter: """ Export documentation to multiple formats with theme support. """ def __init__(self): self.themes = self._load_themes() self.templates = self._load_templates() def export_documentation( self, analysis_data: Dict[str, Any], options: ExportOptions ) -> Dict[str, Any]: """ Export documentation in the specified format. Args: analysis_data: Complete analysis data options: Export configuration Returns: Export result with file path and metadata """ try: logger.info(f"Exporting documentation to {options.format.value} with {options.theme.value} theme") if options.format == ExportFormat.HTML: return self._export_html(analysis_data, options) elif options.format == ExportFormat.PDF: return self._export_pdf(analysis_data, options) elif options.format == ExportFormat.DOCX: return self._export_docx(analysis_data, options) elif options.format == ExportFormat.MARKDOWN: return self._export_markdown(analysis_data, options) elif options.format == ExportFormat.CONFLUENCE: return self._export_confluence(analysis_data, options) elif options.format == ExportFormat.NOTION: return self._export_notion(analysis_data, options) elif options.format == ExportFormat.JSON: return self._export_json(analysis_data, options) elif options.format == ExportFormat.EPUB: return self._export_epub(analysis_data, options) else: raise ValueError(f"Unsupported export format: {options.format}") except Exception as e: logger.error(f"Export failed: {e}") return { 'success': False, 'error': str(e), 'format': options.format.value } def export_multiple_formats( self, analysis_data: Dict[str, Any], formats: List[ExportFormat], theme: Theme = Theme.DEFAULT, output_dir: Optional[str] = None ) -> Dict[str, Dict[str, Any]]: """Export documentation in multiple formats simultaneously.""" results = {} for format_type in formats: options = ExportOptions( format=format_type, theme=theme, output_path=output_dir ) result = self.export_documentation(analysis_data, options) results[format_type.value] = result return results def _export_html(self, analysis_data: Dict[str, Any], options: ExportOptions) -> Dict[str, Any]: """Export to HTML format.""" try: from src.generators.interactive_doc_generator import InteractiveDocumentationGenerator generator = InteractiveDocumentationGenerator() html_content = generator.generate_interactive_docs( analysis_data=analysis_data, title=options.title or "Project Documentation", theme=options.theme.value, include_search=options.include_search, include_navigation=True, include_live_diagrams=options.include_diagrams ) # Apply custom CSS if provided if options.custom_css: html_content = self._apply_custom_css(html_content, options.custom_css) # Save to file output_path = self._get_output_path(options, "documentation.html") with open(output_path, 'w', encoding='utf-8') as f: f.write(html_content) return { 'success': True, 'format': 'html', 'output_path': output_path, 'file_size': len(html_content.encode('utf-8')), 'features': { 'interactive': True, 'searchable': options.include_search, 'responsive': True, 'diagrams': options.include_diagrams } } except Exception as e: return {'success': False, 'error': str(e), 'format': 'html'} def _export_pdf(self, analysis_data: Dict[str, Any], options: ExportOptions) -> Dict[str, Any]: """Export to PDF format.""" try: # First generate HTML html_options = ExportOptions( format=ExportFormat.HTML, theme=options.theme, include_toc=options.include_toc, include_diagrams=options.include_diagrams, include_code_highlighting=options.include_code_highlighting, include_search=False, # Not relevant for PDF title=options.title ) html_result = self._export_html(analysis_data, html_options) if not html_result.get('success'): return html_result # Convert HTML to PDF using weasyprint try: from weasyprint import HTML, CSS html_path = html_result['output_path'] pdf_path = self._get_output_path(options, "documentation.pdf") # Add PDF-specific CSS pdf_css = self._get_pdf_css(options.theme) html_doc = HTML(filename=html_path) css_doc = CSS(string=pdf_css) html_doc.write_pdf(pdf_path, stylesheets=[css_doc]) return { 'success': True, 'format': 'pdf', 'output_path': pdf_path, 'file_size': Path(pdf_path).stat().st_size, 'features': { 'printable': True, 'bookmarks': options.include_toc, 'diagrams': options.include_diagrams } } except ImportError: return { 'success': False, 'error': 'weasyprint not installed. Install with: pip install weasyprint', 'format': 'pdf' } except Exception as e: return {'success': False, 'error': str(e), 'format': 'pdf'} def _export_docx(self, analysis_data: Dict[str, Any], options: ExportOptions) -> Dict[str, Any]: """Export to Word document format.""" try: from docx import Document from docx.shared import Inches doc = Document() # Set document title title = options.title or "Project Documentation" doc_title = doc.add_heading(title, 0) # Add sections based on analysis data self._add_docx_sections(doc, analysis_data, options) # Save document output_path = self._get_output_path(options, "documentation.docx") doc.save(output_path) return { 'success': True, 'format': 'docx', 'output_path': output_path, 'file_size': Path(output_path).stat().st_size, 'features': { 'editable': True, 'formatted': True, 'compatible': True } } except ImportError: return { 'success': False, 'error': 'python-docx not installed. Install with: pip install python-docx', 'format': 'docx' } except Exception as e: return {'success': False, 'error': str(e), 'format': 'docx'} def _export_markdown(self, analysis_data: Dict[str, Any], options: ExportOptions) -> Dict[str, Any]: """Export to Markdown format.""" try: markdown_content = self._generate_markdown_content(analysis_data, options) output_path = self._get_output_path(options, "documentation.md") with open(output_path, 'w', encoding='utf-8') as f: f.write(markdown_content) return { 'success': True, 'format': 'markdown', 'output_path': output_path, 'file_size': len(markdown_content.encode('utf-8')), 'features': { 'portable': True, 'version_controllable': True, 'readable': True, 'diagrams_supported': options.include_diagrams } } except Exception as e: return {'success': False, 'error': str(e), 'format': 'markdown'} def _export_confluence(self, analysis_data: Dict[str, Any], options: ExportOptions) -> Dict[str, Any]: """Export to Confluence wiki format.""" try: confluence_content = self._generate_confluence_content(analysis_data, options) output_path = self._get_output_path(options, "documentation_confluence.txt") with open(output_path, 'w', encoding='utf-8') as f: f.write(confluence_content) return { 'success': True, 'format': 'confluence', 'output_path': output_path, 'file_size': len(confluence_content.encode('utf-8')), 'features': { 'wiki_formatted': True, 'collaborative': True, 'macros_supported': True }, 'instructions': 'Copy content to Confluence page editor' } except Exception as e: return {'success': False, 'error': str(e), 'format': 'confluence'} def _export_notion(self, analysis_data: Dict[str, Any], options: ExportOptions) -> Dict[str, Any]: """Export to Notion format.""" try: notion_blocks = self._generate_notion_blocks(analysis_data, options) output_path = self._get_output_path(options, "notion_blocks.json") with open(output_path, 'w', encoding='utf-8') as f: json.dump(notion_blocks, f, indent=2) return { 'success': True, 'format': 'notion', 'output_path': output_path, 'file_size': Path(output_path).stat().st_size, 'features': { 'block_structured': True, 'interactive': True, 'embeddable': True }, 'instructions': 'Import blocks using Notion API' } except Exception as e: return {'success': False, 'error': str(e), 'format': 'notion'} def _export_json(self, analysis_data: Dict[str, Any], options: ExportOptions) -> Dict[str, Any]: """Export raw data as JSON.""" try: # Enhanced JSON with export metadata export_data = { 'metadata': { 'export_format': 'json', 'theme': options.theme.value, 'generated_at': self._get_timestamp(), 'title': options.title or "Project Documentation" }, 'analysis_data': analysis_data } output_path = self._get_output_path(options, "documentation.json") with open(output_path, 'w', encoding='utf-8') as f: json.dump(export_data, f, indent=2, default=str) return { 'success': True, 'format': 'json', 'output_path': output_path, 'file_size': Path(output_path).stat().st_size, 'features': { 'machine_readable': True, 'structured': True, 'processable': True } } except Exception as e: return {'success': False, 'error': str(e), 'format': 'json'} def _export_epub(self, analysis_data: Dict[str, Any], options: ExportOptions) -> Dict[str, Any]: """Export to EPUB format.""" try: from ebooklib import epub book = epub.EpubBook() # Set metadata title = options.title or "Project Documentation" book.set_identifier('documentation') book.set_title(title) book.set_language('en') book.add_author('MCP Document Automation') # Generate chapters chapters = self._generate_epub_chapters(analysis_data, options) for chapter in chapters: book.add_item(chapter) # Define Table of Contents book.toc = [(epub.Section('Documentation'), chapters)] # Add navigation book.add_item(epub.EpubNcx()) book.add_item(epub.EpubNav()) # Create spine book.spine = ['nav'] + chapters # Save EPUB output_path = self._get_output_path(options, "documentation.epub") epub.write_epub(output_path, book, {}) return { 'success': True, 'format': 'epub', 'output_path': output_path, 'file_size': Path(output_path).stat().st_size, 'features': { 'portable': True, 'reflowable': True, 'accessible': True } } except ImportError: return { 'success': False, 'error': 'EbookLib not installed. Install with: pip install EbookLib', 'format': 'epub' } except Exception as e: return {'success': False, 'error': str(e), 'format': 'epub'} def _generate_markdown_content(self, analysis_data: Dict[str, Any], options: ExportOptions) -> str: """Generate markdown content from analysis data.""" content = [] # Title title = options.title or "Project Documentation" content.append(f"# {title}\n") # Table of contents if options.include_toc: content.append("## Table of Contents\n") content.append("- [Project Overview](#project-overview)") if 'technology_stack' in analysis_data: content.append("- [Technology Stack](#technology-stack)") if 'file_structure' in analysis_data: content.append("- [Project Structure](#project-structure)") if analysis_data.get('api_endpoints'): content.append("- [API Endpoints](#api-endpoints)") if analysis_data.get('database_schemas'): content.append("- [Database Schemas](#database-schemas)") content.append("") # Overview section content.append("## Project Overview\n") content.append("This documentation was automatically generated from codebase analysis.\n") if 'total_files' in analysis_data: content.append(f"- **Total Files**: {analysis_data['total_files']}") if 'analyzed_files' in analysis_data: content.append(f"- **Analyzed Files**: {analysis_data['analyzed_files']}") content.append("") # Technology Stack if 'technology_stack' in analysis_data: content.append("## Technology Stack\n") tech_stack = analysis_data['technology_stack'] for category, items in tech_stack.items(): if items and category != 'confidence_scores': content.append(f"### {category.replace('_', ' ').title()}") if isinstance(items, list): for item in items: content.append(f"- {item}") else: content.append(f"- {items}") content.append("") # File Structure if 'file_structure' in analysis_data: content.append("## Project Structure\n") structure = analysis_data['file_structure'] content.append(f"**Statistics:**") content.append(f"- Total Lines: {structure.get('total_lines', 0):,}") content.append(f"- File Types: {len(structure.get('file_types', {}))}") content.append("") if structure.get('file_types'): content.append("**File Types:**") for ext, count in structure['file_types'].items(): content.append(f"- `{ext or 'no extension'}`: {count} files") content.append("") # API Endpoints if analysis_data.get('api_endpoints'): content.append("## API Endpoints\n") endpoints = analysis_data['api_endpoints'] for endpoint in endpoints[:20]: # Limit display methods = ', '.join(endpoint.get('methods', ['GET'])) content.append(f"### `{methods}` {endpoint.get('path', '')}") content.append(f"- **File**: `{endpoint.get('file', '')}`") content.append(f"- **Framework**: {endpoint.get('framework', 'unknown')}") content.append("") # Database Schemas if analysis_data.get('database_schemas'): content.append("## Database Schemas\n") schemas = analysis_data['database_schemas'] for i, schema in enumerate(schemas, 1): content.append(f"### Schema {i}") content.append(f"- **Type**: {schema.get('schema_type', 'Unknown')}") content.append(f"- **Tables**: {len(schema.get('tables', []))}") if schema.get('tables'): content.append("**Tables:**") for table in schema['tables'][:10]: content.append(f"- `{table}`") if len(schema['tables']) > 10: content.append(f"- ... and {len(schema['tables']) - 10} more") content.append("") # Dependencies if analysis_data.get('dependencies'): deps = analysis_data['dependencies'] if deps.get('dependencies'): content.append("## Dependencies\n") for dep, version in list(deps['dependencies'].items())[:15]: content.append(f"- `{dep}`: {version}") if len(deps['dependencies']) > 15: content.append(f"- ... and {len(deps['dependencies']) - 15} more") content.append("") # Mermaid diagrams if options.include_diagrams and analysis_data: content.append("## Architecture Diagram\n") content.append("```mermaid") content.append("graph TD") content.append(" A[Project Root] --> B[Source Code]") content.append(" A --> C[Configuration]") content.append(" A --> D[Documentation]") content.append("```\n") return '\n'.join(content) def _generate_confluence_content(self, analysis_data: Dict[str, Any], options: ExportOptions) -> str: """Generate Confluence wiki markup.""" content = [] title = options.title or "Project Documentation" content.append(f"h1. {title}\n") content.append("This documentation was automatically generated from codebase analysis.\n") if 'technology_stack' in analysis_data: content.append("h2. Technology Stack\n") tech_stack = analysis_data['technology_stack'] for category, items in tech_stack.items(): if items and category != 'confidence_scores': content.append(f"h3. {category.replace('_', ' ').title()}") if isinstance(items, list): for item in items: content.append(f"* {item}") else: content.append(f"* {items}") content.append("") return '\n'.join(content) def _generate_notion_blocks(self, analysis_data: Dict[str, Any], options: ExportOptions) -> List[Dict[str, Any]]: """Generate Notion blocks structure.""" blocks = [] # Title block blocks.append({ "type": "heading_1", "heading_1": { "rich_text": [{"type": "text", "text": {"content": options.title or "Project Documentation"}}] } }) # Overview paragraph blocks.append({ "type": "paragraph", "paragraph": { "rich_text": [{"type": "text", "text": {"content": "This documentation was automatically generated from codebase analysis."}}] } }) return blocks def _generate_epub_chapters(self, analysis_data: Dict[str, Any], options: ExportOptions) -> List: """Generate EPUB chapters.""" from ebooklib import epub chapters = [] # Overview chapter chapter1 = epub.EpubHtml(title='Overview', file_name='overview.xhtml', lang='en') chapter1.content = f''' <html> <head><title>Overview</title></head> <body> <h1>Project Overview</h1> <p>This documentation was automatically generated from codebase analysis.</p> </body> </html>''' chapters.append(chapter1) return chapters def _add_docx_sections(self, doc, analysis_data: Dict[str, Any], options: ExportOptions) -> None: """Add sections to Word document.""" # Overview section doc.add_heading('Project Overview', level=1) doc.add_paragraph('This documentation was automatically generated from codebase analysis.') # Add more sections based on analysis_data... if 'technology_stack' in analysis_data: doc.add_heading('Technology Stack', level=1) tech_stack = analysis_data['technology_stack'] for category, items in tech_stack.items(): if items and category != 'confidence_scores': doc.add_heading(category.replace('_', ' ').title(), level=2) if isinstance(items, list): for item in items: p = doc.add_paragraph() p.add_run(f"- {item}") else: p = doc.add_paragraph() p.add_run(f"- {items}") def _get_output_path(self, options: ExportOptions, default_filename: str) -> str: """Get output file path.""" if options.output_path: if Path(options.output_path).is_dir(): return str(Path(options.output_path) / default_filename) else: return options.output_path else: return default_filename def _get_timestamp(self) -> str: """Get current timestamp.""" from datetime import datetime return datetime.now().isoformat() def _apply_custom_css(self, html_content: str, custom_css: str) -> str: """Apply custom CSS to HTML content.""" # Find closing </style> tag and insert custom CSS before it style_end = html_content.find('</style>') if style_end != -1: return html_content[:style_end] + custom_css + html_content[style_end:] return html_content def _get_pdf_css(self, theme: Theme) -> str: """Get PDF-specific CSS.""" return ''' @page { margin: 1in; @bottom-center { content: counter(page); } } body { font-size: 12pt; line-height: 1.4; } .header { display: none; } .sidebar { display: none; } .main-content { margin: 0; padding: 0; } ''' def _load_themes(self) -> Dict[str, Dict[str, str]]: """Load theme configurations.""" return { 'default': {'primary_color': '#007bff', 'background': '#ffffff'}, 'dark': {'primary_color': '#0d6efd', 'background': '#1a1a1a'}, 'minimal': {'primary_color': '#666666', 'background': '#ffffff'}, 'corporate': {'primary_color': '#2c3e50', 'background': '#f8f9fa'}, 'github': {'primary_color': '#0969da', 'background': '#ffffff'}, 'material': {'primary_color': '#1976d2', 'background': '#fafafa'} } def _load_templates(self) -> Dict[str, str]: """Load export templates.""" return { 'html': 'HTML template', 'markdown': 'Markdown template', 'confluence': 'Confluence template' }

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/vedantparmar12/Document-Automation'

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