""" Export service for generating documents in various formats. """ import os import json from datetime import datetime from typing import Dict, Any from reportlab.lib.pagesizes import letter from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle from reportlab.lib.units import inch from docx import Document from docx.shared import Inches import logging from config import settings logger = logging.getLogger(__name__) class ExportService: """Service for exporting projects to various formats.""" @staticmethod def ensure_upload_directory(): """Ensure upload directory exists.""" os.makedirs(settings.upload_dir, exist_ok=True) @staticmethod async def export_to_pdf(project_data: Dict[str, Any], file_path: str) -> str: """Export project to PDF format.""" try: ExportService.ensure_upload_directory() # Create PDF document doc = SimpleDocTemplate(file_path, pagesize=letter) story = [] styles = getSampleStyleSheet() # Custom styles title_style = ParagraphStyle( 'CustomTitle', parent=styles['Heading1'], fontSize=24, spaceAfter=30, alignment=1 # Center alignment ) phase_title_style = ParagraphStyle( 'PhaseTitle', parent=styles['Heading2'], fontSize=16, spaceBefore=20, spaceAfter=10 ) # Add title story.append(Paragraph(project_data['title'], title_style)) story.append(Spacer(1, 20)) # Add description if project_data.get('description'): story.append(Paragraph(f"Description: {project_data['description']}", styles['Normal'])) story.append(Spacer(1, 20)) # Add metadata story.append(Paragraph(f"Created: {project_data['created_at']}", styles['Normal'])) story.append(Paragraph(f"Owner: {project_data['owner']['full_name']}", styles['Normal'])) story.append(Spacer(1, 30)) # Add phases for phase in project_data['phases']: # Phase title story.append(Paragraph(f"Phase {phase['phase_number']}: {phase['title']}", phase_title_style)) # Phase description if phase.get('description'): story.append(Paragraph(f"{phase['description']}", styles['Italic'])) story.append(Spacer(1, 10)) # User input if phase.get('user_input'): story.append(Paragraph("Input:", styles['Normal'])) story.append(Paragraph(phase['user_input'], styles['Normal'])) story.append(Spacer(1, 10)) # AI response if phase.get('ai_response'): story.append(Paragraph("Response:", styles['Normal'])) story.append(Paragraph(phase['ai_response'], styles['Normal'])) else: story.append(Paragraph("Phase not completed", styles['Italic'])) story.append(Spacer(1, 20)) # Build PDF doc.build(story) logger.info(f"PDF export completed: {file_path}") return file_path except Exception as e: logger.error(f"PDF export error: {e}") raise Exception(f"Failed to export to PDF: {str(e)}") @staticmethod async def export_to_word(project_data: Dict[str, Any], file_path: str) -> str: """Export project to Word format.""" try: ExportService.ensure_upload_directory() # Create Word document doc = Document() # Title title = doc.add_heading(project_data['title'], 0) from docx.enum.text import WD_PARAGRAPH_ALIGNMENT title.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER # Description if project_data.get('description'): doc.add_paragraph(f"Description: {project_data['description']}") # Metadata doc.add_paragraph(f"Created: {project_data['created_at']}") doc.add_paragraph(f"Owner: {project_data['owner']['full_name']}") doc.add_paragraph("") # Empty line # Phases for phase in project_data['phases']: # Phase heading doc.add_heading(f"Phase {phase['phase_number']}: {phase['title']}", 1) # Phase description if phase.get('description'): p = doc.add_paragraph() p.add_run(phase['description']).italic = True # User input if phase.get('user_input'): doc.add_paragraph("Input:", style='Heading 2') doc.add_paragraph(phase['user_input']) # AI response if phase.get('ai_response'): doc.add_paragraph("Response:", style='Heading 2') doc.add_paragraph(phase['ai_response']) else: p = doc.add_paragraph() p.add_run("Phase not completed").italic = True doc.add_paragraph("") # Empty line # Save document doc.save(file_path) logger.info(f"Word export completed: {file_path}") return file_path except Exception as e: logger.error(f"Word export error: {e}") raise Exception(f"Failed to export to Word: {str(e)}") @staticmethod async def export_to_json(project_data: Dict[str, Any], file_path: str) -> str: """Export project to JSON format.""" try: ExportService.ensure_upload_directory() # Prepare export data export_data = { "export_info": { "exported_at": datetime.now().isoformat(), "format": "json", "version": "1.0" }, "project": project_data } # Write JSON file with open(file_path, 'w', encoding='utf-8') as f: json.dump(export_data, f, indent=2, ensure_ascii=False, default=str) logger.info(f"JSON export completed: {file_path}") return file_path except Exception as e: logger.error(f"JSON export error: {e}") raise Exception(f"Failed to export to JSON: {str(e)}") @staticmethod def get_export_file_path(export_id: str, format: str) -> str: """Generate file path for export.""" ExportService.ensure_upload_directory() filename = f"export_{export_id}.{format}" return os.path.join(settings.upload_dir, filename)