PDF_Generation_and_Automation/python-pdf-generator/salesforce_pdf_api.py
2025-08-25 21:52:12 +05:30

378 lines
11 KiB
Python

#!/usr/bin/env python3
"""
Flask API for Salesforce PDF Generation
Takes HTML content from Salesforce and returns a downloadable PDF
"""
from flask import Flask, request, send_file, jsonify
from flask_cors import CORS
import pdfkit
import os
import tempfile
import base64
from datetime import datetime
import logging
import json
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
app = Flask(__name__)
CORS(app)
# Configure pdfkit options for better PDF generation
PDF_OPTIONS = {
'page-size': 'A4',
'margin-top': '0.75in',
'margin-right': '0.75in',
'margin-bottom': '0.75in',
'margin-left': '0.75in',
'encoding': "UTF-8",
'no-outline': None,
'enable-local-file-access': None,
'print-media-type': None,
'dpi': 300,
'image-quality': 100,
'javascript-delay': 1000,
'no-stop-slow-scripts': None,
'custom-header': [
('Accept-Encoding', 'gzip')
]
}
@app.route('/health', methods=['GET'])
def health_check():
"""Health check endpoint"""
return jsonify({
'status': 'healthy',
'timestamp': datetime.now().isoformat(),
'service': 'Salesforce PDF Generator API'
})
@app.route('/generate-pdf', methods=['POST'])
def generate_pdf():
"""
Generate PDF from HTML content sent from Salesforce
Expected JSON payload:
{
"html_content": "<html>...</html>",
"property_data": {...},
"template_name": "everkind",
"filename": "property_brochure.pdf"
}
"""
try:
logger.info("Received PDF generation request from Salesforce")
# Get request data
data = request.get_json()
if not data:
return jsonify({'error': 'No data provided'}), 400
html_content = data.get('html_content')
property_data = data.get('property_data', {})
template_name = data.get('template_name', 'default')
filename = data.get('filename', f'property_brochure_{datetime.now().strftime("%Y%m%d_%H%M%S")}.pdf')
if not html_content:
return jsonify({'error': 'HTML content is required'}), 400
logger.info(f"Processing template: {template_name}")
logger.info(f"Property data keys: {list(property_data.keys()) if property_data else 'None'}")
logger.info(f"HTML content length: {len(html_content)}")
# Create complete HTML document with proper styling
complete_html = create_complete_html_document(html_content, property_data, template_name)
# Generate PDF
pdf_path = generate_pdf_from_html(complete_html, filename)
if not pdf_path or not os.path.exists(pdf_path):
return jsonify({'error': 'Failed to generate PDF'}), 500
logger.info(f"PDF generated successfully: {pdf_path}")
# Return the PDF file
return send_file(
pdf_path,
as_attachment=True,
download_name=filename,
mimetype='application/pdf'
)
except Exception as e:
logger.error(f"Error generating PDF: {str(e)}")
return jsonify({'error': f'PDF generation failed: {str(e)}'}), 500
def create_complete_html_document(html_content, property_data, template_name):
"""Create a complete HTML document with proper styling and data"""
# Base CSS styles for consistent PDF output
base_css = """
<style>
@page {
size: A4;
margin: 0.75in;
}
body {
font-family: 'Arial', 'Helvetica', sans-serif;
line-height: 1.6;
color: #333;
margin: 0;
padding: 0;
background: white;
}
.property-header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
border-radius: 10px;
margin-bottom: 25px;
page-break-inside: avoid;
}
.property-header h1 {
margin: 0;
font-size: 28px;
font-weight: bold;
text-shadow: 0 2px 4px rgba(0,0,0,0.3);
}
.property-header p {
margin: 10px 0 0 0;
font-size: 16px;
opacity: 0.95;
}
.property-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 15px;
margin-bottom: 25px;
page-break-inside: avoid;
}
.property-card {
background: #f8f9fa;
padding: 15px;
border-radius: 8px;
border: 1px solid #dee2e6;
text-align: center;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.property-card-label {
font-weight: bold;
color: #667eea;
font-size: 12px;
text-transform: uppercase;
margin-bottom: 5px;
}
.property-card-value {
font-size: 16px;
color: #333;
font-weight: 600;
}
.property-details {
background: white;
padding: 20px;
border-radius: 10px;
border: 1px solid #dee2e6;
margin-bottom: 25px;
page-break-inside: avoid;
}
.property-details h2 {
color: #667eea;
margin-bottom: 15px;
font-size: 18px;
border-bottom: 2px solid #667eea;
padding-bottom: 5px;
}
.property-details-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
font-size: 14px;
}
.content-section {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
margin-bottom: 25px;
page-break-inside: avoid;
}
.content-section h2 {
color: #667eea;
margin-bottom: 15px;
font-size: 18px;
}
.footer {
text-align: center;
color: #666;
font-size: 12px;
border-top: 1px solid #dee2e6;
padding-top: 15px;
margin-top: 30px;
page-break-inside: avoid;
}
/* Ensure proper page breaks */
.page-break {
page-break-before: always;
}
/* Print-specific styles */
@media print {
body { margin: 0; }
.property-header { break-inside: avoid; }
.property-grid { break-inside: avoid; }
.property-details { break-inside: avoid; }
}
</style>
"""
# Create the complete HTML document
complete_html = f"""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Property Brochure - {property_data.get('propertyName', 'Property')}</title>
{base_css}
</head>
<body>
{html_content}
<div class="footer">
<p><strong>Generated on:</strong> {datetime.now().strftime('%B %d, %Y at %I:%M %p')}</p>
<p><em>Property CRM System - Professional Brochure</em></p>
<p><em>Template: {template_name}</em></p>
</div>
</body>
</html>
"""
return complete_html
def generate_pdf_from_html(html_content, filename):
"""Generate PDF from HTML content using pdfkit"""
try:
# Create temporary file for the PDF
temp_dir = tempfile.gettempdir()
pdf_path = os.path.join(temp_dir, filename)
logger.info(f"Generating PDF at: {pdf_path}")
# Configure pdfkit with wkhtmltopdf
try:
# Try to use system wkhtmltopdf
config = pdfkit.configuration(wkhtmltopdf='/usr/bin/wkhtmltopdf')
pdfkit.from_string(html_content, pdf_path, options=PDF_OPTIONS, configuration=config)
except Exception as e:
logger.warning(f"System wkhtmltopdf failed: {e}")
# Try without configuration (uses PATH)
pdfkit.from_string(html_content, pdf_path, options=PDF_OPTIONS)
if os.path.exists(pdf_path):
file_size = os.path.getsize(pdf_path)
logger.info(f"PDF generated successfully. Size: {file_size} bytes")
return pdf_path
else:
logger.error("PDF file was not created")
return None
except Exception as e:
logger.error(f"Error in generate_pdf_from_html: {str(e)}")
return None
@app.route('/test-pdf', methods=['GET'])
def test_pdf():
"""Test endpoint to verify PDF generation works"""
try:
test_html = """
<div class="property-header">
<h1>Test Property</h1>
<p>Villa in Dubai Marina</p>
</div>
<div class="property-grid">
<div class="property-card">
<div class="property-card-label">Price</div>
<div class="property-card-value">AED 2,500,000</div>
</div>
<div class="property-card">
<div class="property-card-label">Bedrooms</div>
<div class="property-card-value">3</div>
</div>
</div>
"""
test_data = {
'propertyName': 'Test Property',
'propertyType': 'Villa',
'location': 'Dubai Marina'
}
complete_html = create_complete_html_document(test_html, test_data, 'test')
pdf_path = generate_pdf_from_html(complete_html, 'test_property.pdf')
if pdf_path and os.path.exists(pdf_path):
return send_file(
pdf_path,
as_attachment=True,
download_name='test_property.pdf',
mimetype='application/pdf'
)
else:
return jsonify({'error': 'Test PDF generation failed'}), 500
except Exception as e:
return jsonify({'error': f'Test failed: {str(e)}'}), 500
@app.route('/api/info', methods=['GET'])
def api_info():
"""Get API information"""
return jsonify({
'name': 'Salesforce PDF Generator API',
'version': '1.0.0',
'description': 'Takes HTML content from Salesforce and returns downloadable PDF',
'endpoints': {
'health': '/health',
'generate_pdf': '/generate-pdf',
'test_pdf': '/test-pdf',
'api_info': '/api/info'
},
'usage': {
'method': 'POST',
'endpoint': '/generate-pdf',
'content_type': 'application/json',
'payload': {
'html_content': 'HTML content from Salesforce',
'property_data': 'Property information object',
'template_name': 'Template name',
'filename': 'Output filename (optional)'
}
}
})
if __name__ == '__main__':
logger.info("Starting Salesforce PDF Generator API Server...")
logger.info("Available endpoints:")
logger.info(" GET /health - Health check")
logger.info(" POST /generate-pdf - Generate PDF from HTML")
logger.info(" GET /test-pdf - Test PDF generation")
logger.info(" GET /api/info - API information")
app.run(host='0.0.0.0', port=8000, debug=True)