🔍 Code Extractor

function export_to_pdf_v1

Maturity: 50

Flask route handler that exports a chat conversation to a PDF file with formatted messages, roles, and references using the reportlab library.

File:
/tf/active/vicechatdev/docchat/app.py
Lines:
1839 - 1962
Complexity:
moderate

Purpose

This function provides a PDF export feature for chat conversations in a DocChat application. It receives a conversation history via POST request, formats it with custom styles (different colors for user/assistant messages), includes references when present, and returns a downloadable PDF file. The function is protected by login_required decorator and handles errors gracefully, including missing reportlab dependency.

Source Code

def export_to_pdf():
    """Export chat conversation to PDF"""
    try:
        from reportlab.lib.pagesizes import letter
        from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
        from reportlab.lib.units import inch
        from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak
        from reportlab.lib.colors import HexColor
        from io import BytesIO
        
        data = request.get_json()
        conversation = data.get('conversation', [])
        
        if not conversation:
            return jsonify({'error': 'No conversation to export'}), 400
        
        # Create PDF
        file_stream = BytesIO()
        doc = SimpleDocTemplate(file_stream, pagesize=letter,
                              topMargin=0.75*inch, bottomMargin=0.75*inch)
        
        # Define styles
        styles = getSampleStyleSheet()
        
        title_style = ParagraphStyle(
            'CustomTitle',
            parent=styles['Heading1'],
            fontSize=24,
            textColor=HexColor('#2563eb'),
            spaceAfter=30,
            alignment=1  # Center
        )
        
        date_style = ParagraphStyle(
            'DateStyle',
            parent=styles['Normal'],
            fontSize=10,
            textColor=HexColor('#808080'),
            alignment=1  # Center
        )
        
        user_style = ParagraphStyle(
            'UserStyle',
            parent=styles['Heading2'],
            fontSize=14,
            textColor=HexColor('#2563eb'),
            spaceAfter=6
        )
        
        assistant_style = ParagraphStyle(
            'AssistantStyle',
            parent=styles['Heading2'],
            fontSize=14,
            textColor=HexColor('#10b981'),
            spaceAfter=6
        )
        
        content_style = ParagraphStyle(
            'ContentStyle',
            parent=styles['Normal'],
            fontSize=11,
            leading=16
        )
        
        ref_style = ParagraphStyle(
            'RefStyle',
            parent=styles['Normal'],
            fontSize=9,
            leftIndent=20
        )
        
        # Build document
        story = []
        
        # Add title
        story.append(Paragraph('DocChat Conversation History', title_style))
        story.append(Paragraph(
            f'Exported on {datetime.now().strftime("%B %d, %Y at %H:%M")}',
            date_style
        ))
        story.append(Spacer(1, 0.5*inch))
        
        # Add conversation
        for msg in conversation:
            role = msg.get('role', 'user')
            content = msg.get('content', '')
            
            # Add role heading
            if role == 'user':
                story.append(Paragraph('You', user_style))
            else:
                story.append(Paragraph('Assistant', assistant_style))
            
            # Add content (escape HTML special chars)
            escaped_content = content.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
            story.append(Paragraph(escaped_content, content_style))
            
            # Add references if present
            if role == 'assistant' and 'references' in msg and msg['references']:
                story.append(Spacer(1, 0.1*inch))
                story.append(Paragraph('<b>References:</b>', ref_style))
                for i, ref in enumerate(msg['references'], 1):
                    ref_text = f"{i}. {ref.get('file_name', 'Unknown document')}"
                    story.append(Paragraph(ref_text, ref_style))
            
            story.append(Spacer(1, 0.3*inch))
        
        # Build PDF
        doc.build(story)
        file_stream.seek(0)
        
        return send_file(
            file_stream,
            mimetype='application/pdf',
            as_attachment=True,
            download_name=f'chat-export-{datetime.now().strftime("%Y%m%d")}.pdf'
        )
        
    except ImportError:
        logger.error("reportlab not installed")
        return jsonify({'error': 'PDF export requires reportlab library'}), 500
    except Exception as e:
        logger.error(f"Error exporting to PDF: {e}")
        return jsonify({'error': str(e)}), 500

Return Value

Returns a Flask Response object containing either: (1) A PDF file (mimetype='application/pdf') with filename format 'chat-export-YYYYMMDD.pdf' for successful exports, (2) A JSON error response with status 400 if conversation is empty, (3) A JSON error response with status 500 if reportlab is not installed or other exceptions occur. The PDF contains formatted conversation with title, export date, role-based colored headings, message content, and references.

Dependencies

  • flask
  • reportlab
  • logging
  • datetime
  • io

Required Imports

from flask import request
from flask import jsonify
from flask import send_file
from datetime import datetime
import logging

Conditional/Optional Imports

These imports are only needed under specific conditions:

from reportlab.lib.pagesizes import letter

Condition: Required for PDF generation, imported lazily inside function to handle missing dependency gracefully

Required (conditional)
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle

Condition: Required for PDF styling, imported lazily inside function

Required (conditional)
from reportlab.lib.units import inch

Condition: Required for PDF layout measurements, imported lazily inside function

Required (conditional)
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, PageBreak

Condition: Required for PDF document structure, imported lazily inside function

Required (conditional)
from reportlab.lib.colors import HexColor

Condition: Required for custom color styling in PDF, imported lazily inside function

Required (conditional)
from io import BytesIO

Condition: Required for in-memory file handling, imported lazily inside function

Required (conditional)

Usage Example

# Client-side usage (JavaScript fetch example):
const conversation = [
  {role: 'user', content: 'What is machine learning?'},
  {role: 'assistant', content: 'Machine learning is...', references: [{file_name: 'ml_guide.pdf'}]}
];

fetch('/api/export/pdf', {
  method: 'POST',
  headers: {'Content-Type': 'application/json'},
  body: JSON.stringify({conversation: conversation})
})
.then(response => response.blob())
.then(blob => {
  const url = window.URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'chat-export.pdf';
  a.click();
});

# Server-side testing:
import requests
response = requests.post('http://localhost:5000/api/export/pdf',
  json={'conversation': [{'role': 'user', 'content': 'Hello'}]},
  cookies={'session': 'valid_session_token'})
with open('export.pdf', 'wb') as f:
  f.write(response.content)

Best Practices

  • Always ensure reportlab is installed before deploying this feature (pip install reportlab)
  • The function escapes HTML special characters (&, <, >) to prevent PDF rendering issues
  • Validate conversation data on client-side before sending to reduce 400 errors
  • The function uses BytesIO for in-memory file handling to avoid disk I/O
  • Error handling includes specific ImportError for missing reportlab dependency
  • PDF styling uses HexColor for consistent branding (blue for user, green for assistant)
  • The login_required decorator must be properly configured to protect this endpoint
  • Consider adding rate limiting to prevent abuse of PDF generation
  • Large conversations may cause memory issues; consider pagination or size limits
  • The function uses secure filename generation with timestamp to avoid conflicts

Related Versions

Other versions of this component:

  • export_to_pdf_v1

    From: /tf/active/vicechatdev/vice_ai/complex_app.py | Maturity: N/A

Similar Components

AI-powered semantic similarity - components with related functionality:

  • function export_report 73.6% similar

    Flask route handler that exports an analysis report for a given session in either PDF or Word format, retrieving session data and generating a downloadable file.

    From: /tf/active/vicechatdev/full_smartstat/app.py
  • function export_to_word 72.6% similar

    Flask route handler that exports a chat conversation to a formatted Microsoft Word (.docx) document with styled headings, timestamps, and references.

    From: /tf/active/vicechatdev/docchat/app.py
  • function export_to_pdf 67.4% similar

    Exports a document with text and data sections to a PDF file using ReportLab, handling custom styling, section ordering, and content formatting including Quill Delta to HTML/Markdown conversion.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
  • function export_document 66.8% similar

    Flask route handler that exports a document in either DOCX or PDF format, verifying user ownership and document access before generating the export file.

    From: /tf/active/vicechatdev/vice_ai/new_app.py
  • function api_export_document 66.2% similar

    Flask API endpoint that exports a document in either DOCX or PDF format, with authentication and authorization checks.

    From: /tf/active/vicechatdev/vice_ai/complex_app.py
← Back to Browse