Fix 503: make xhtml2pdf import lazy to prevent app crash
If xhtml2pdf fails to install on Flatlogic's server (missing C libraries), the top-level import crashed the entire WSGI app. Now it imports lazily inside render_to_pdf() so the app starts even without xhtml2pdf — only PDF generation degrades gracefully. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
71723dcaf4
commit
74cd93fede
@ -1,10 +1,17 @@
|
|||||||
# === PDF GENERATION ===
|
# === PDF GENERATION ===
|
||||||
# Converts a Django HTML template into a PDF file using xhtml2pdf.
|
# Converts a Django HTML template into a PDF file using xhtml2pdf.
|
||||||
# Used for payslip and receipt PDF attachments sent via email.
|
# Used for payslip and receipt PDF attachments sent via email.
|
||||||
|
#
|
||||||
|
# IMPORTANT: xhtml2pdf is imported LAZILY (inside the function, not at the
|
||||||
|
# top of the file). This is intentional — if xhtml2pdf fails to install on
|
||||||
|
# the server (missing C libraries), the rest of the app still works.
|
||||||
|
# Only PDF generation will fail gracefully.
|
||||||
|
|
||||||
|
import logging
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
from django.template.loader import get_template
|
from django.template.loader import get_template
|
||||||
from xhtml2pdf import pisa
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
def render_to_pdf(template_src, context_dict=None):
|
def render_to_pdf(template_src, context_dict=None):
|
||||||
@ -21,6 +28,17 @@ def render_to_pdf(template_src, context_dict=None):
|
|||||||
if context_dict is None:
|
if context_dict is None:
|
||||||
context_dict = {}
|
context_dict = {}
|
||||||
|
|
||||||
|
# --- Lazy import: only load xhtml2pdf when actually generating a PDF ---
|
||||||
|
# This prevents the entire app from crashing if xhtml2pdf isn't installed.
|
||||||
|
try:
|
||||||
|
from xhtml2pdf import pisa
|
||||||
|
except ImportError:
|
||||||
|
logger.error(
|
||||||
|
"xhtml2pdf is not installed — cannot generate PDF. "
|
||||||
|
"Install it with: pip install xhtml2pdf"
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
# Load and render the HTML template
|
# Load and render the HTML template
|
||||||
template = get_template(template_src)
|
template = get_template(template_src)
|
||||||
html = template.render(context_dict)
|
html = template.render(context_dict)
|
||||||
|
|||||||
@ -22,7 +22,9 @@ from django.conf import settings
|
|||||||
|
|
||||||
from .models import Worker, Project, WorkLog, Team, PayrollRecord, Loan, PayrollAdjustment
|
from .models import Worker, Project, WorkLog, Team, PayrollRecord, Loan, PayrollAdjustment
|
||||||
from .forms import AttendanceLogForm, PayrollAdjustmentForm
|
from .forms import AttendanceLogForm, PayrollAdjustmentForm
|
||||||
from .utils import render_to_pdf
|
# NOTE: render_to_pdf is NOT imported here at the top level.
|
||||||
|
# It's imported lazily inside process_payment() to avoid crashing the
|
||||||
|
# entire app if xhtml2pdf is not installed on the server.
|
||||||
|
|
||||||
|
|
||||||
# === PAYROLL CONSTANTS ===
|
# === PAYROLL CONSTANTS ===
|
||||||
@ -812,6 +814,10 @@ def process_payment(request, worker_id):
|
|||||||
# EMAIL PAYSLIP (outside the transaction — if email fails, payment is
|
# EMAIL PAYSLIP (outside the transaction — if email fails, payment is
|
||||||
# still saved. We don't want a network error to roll back a real payment.)
|
# still saved. We don't want a network error to roll back a real payment.)
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
|
|
||||||
|
# Lazy import — avoids crashing the app if xhtml2pdf isn't installed
|
||||||
|
from .utils import render_to_pdf
|
||||||
|
|
||||||
subject = f"Payslip for {worker.name} - {payroll_record.date}"
|
subject = f"Payslip for {worker.name} - {payroll_record.date}"
|
||||||
|
|
||||||
# Context for both the HTML email body and the PDF attachment
|
# Context for both the HTML email body and the PDF attachment
|
||||||
@ -827,7 +833,7 @@ def process_payment(request, worker_id):
|
|||||||
html_message = render_to_string('core/email/payslip_email.html', email_context)
|
html_message = render_to_string('core/email/payslip_email.html', email_context)
|
||||||
plain_message = strip_tags(html_message)
|
plain_message = strip_tags(html_message)
|
||||||
|
|
||||||
# 2. Render PDF attachment
|
# 2. Render PDF attachment (returns None if xhtml2pdf is not installed)
|
||||||
pdf_content = render_to_pdf('core/pdf/payslip_pdf.html', email_context)
|
pdf_content = render_to_pdf('core/pdf/payslip_pdf.html', email_context)
|
||||||
|
|
||||||
# 3. Send email with PDF attached
|
# 3. Send email with PDF attached
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user