agdar/PDF_IMPLEMENTATION_GUIDE.md
Marwan Alwali 25c9701c34 update
2025-11-06 18:18:43 +03:00

11 KiB

PDF Implementation Guide

This guide explains how to implement PDF generation (view, download, email) for all clinical documents using the reusable BasePDFGenerator service.

Overview

We've created a centralized PDF service (core/pdf_service.py) that provides:

  • Tenant branding (logo + name)
  • Arabic font support with proper text rendering
  • Bilingual labels (English/Arabic)
  • Consistent professional styling
  • Email functionality with PDF attachment
  • View inline or download options

Implementation Steps for Each Module

Step 1: Create PDF Generator Class

In each module's views.py, create a PDF generator class that extends BasePDFGenerator:

from core.pdf_service import BasePDFGenerator

class MedicalConsultationPDFGenerator(BasePDFGenerator):
    """PDF generator for Medical Consultation (MD-F-1)."""
    
    def get_document_title(self):
        """Return document title in English and Arabic."""
        consultation = self.document
        return (
            f"Medical Consultation - {consultation.patient.mrn}",
            "استشارة طبية"
        )
    
    def get_pdf_filename(self):
        """Return PDF filename."""
        consultation = self.document
        return f"medical_consultation_{consultation.patient.mrn}_{consultation.consultation_date}.pdf"
    
    def get_document_sections(self):
        """Return document sections to render."""
        consultation = self.document
        patient = consultation.patient
        
        sections = []
        
        # Patient Information Section
        sections.append({
            'heading_en': 'Patient Information',
            'heading_ar': 'معلومات المريض',
            'type': 'table',
            'content': [
                ('Name', 'الاسم', f"{patient.first_name_en} {patient.last_name_en}", 
                 f"{patient.first_name_ar} {patient.last_name_ar}" if patient.first_name_ar else ""),
                ('MRN', 'رقم السجل الطبي', patient.mrn, ""),
                ('Date of Birth', 'تاريخ الميلاد', patient.date_of_birth.strftime('%Y-%m-%d'), ""),
                ('Gender', 'الجنس', patient.get_sex_display(), ""),
            ]
        })
        
        # Consultation Details Section
        sections.append({
            'heading_en': 'Consultation Details',
            'heading_ar': 'تفاصيل الاستشارة',
            'type': 'table',
            'content': [
                ('Date', 'التاريخ', consultation.consultation_date.strftime('%Y-%m-%d'), ""),
                ('Provider', 'مقدم الخدمة', consultation.provider.get_full_name(), ""),
                ('Chief Complaint', 'الشكوى الرئيسية', consultation.chief_complaint or 'N/A', ""),
            ]
        })
        
        # Add more sections as needed...
        
        return sections

Step 2: Create PDF View

from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.views import View
from core.mixins import TenantFilterMixin

class MedicalConsultationPDFView(LoginRequiredMixin, TenantFilterMixin, View):
    """Generate PDF for medical consultation."""
    
    def get(self, request, pk):
        """Generate and return PDF."""
        # Get consultation
        consultation = get_object_or_404(
            MedicalConsultation.objects.select_related(
                'patient', 'provider', 'tenant'
            ),
            pk=pk,
            tenant=request.user.tenant
        )
        
        # Create PDF generator
        pdf_generator = MedicalConsultationPDFGenerator(consultation, request)
        
        # Get view mode from query parameter
        view_mode = request.GET.get('view', 'download')
        
        # Generate and return PDF
        return pdf_generator.generate_pdf(view_mode=view_mode)

Step 3: Create Email PDF View

from django.contrib import messages
from django.shortcuts import redirect
from django.utils.translation import gettext as _

class MedicalConsultationEmailPDFView(LoginRequiredMixin, TenantFilterMixin, View):
    """Email medical consultation PDF to patient."""
    
    def post(self, request, pk):
        """Send PDF via email."""
        # Get consultation
        consultation = get_object_or_404(
            MedicalConsultation.objects.select_related(
                'patient', 'provider', 'tenant'
            ),
            pk=pk,
            tenant=request.user.tenant
        )
        
        # Get form data
        email_address = request.POST.get('email_address', '').strip()
        custom_message = request.POST.get('email_message', '').strip()
        
        if not email_address:
            messages.error(request, _('Email address is required.'))
            return redirect('medical:consultation_detail', pk=pk)
        
        # Create PDF generator
        pdf_generator = MedicalConsultationPDFGenerator(consultation, request)
        
        # Prepare email content
        subject = f"Medical Consultation - {consultation.patient.mrn}"
        body = f"""
Dear {consultation.patient.first_name_en} {consultation.patient.last_name_en},

Please find attached your medical consultation details.

Consultation Date: {consultation.consultation_date.strftime('%Y-%m-%d')}
Provider: {consultation.provider.get_full_name()}

Best regards,
{consultation.tenant.name}
"""
        
        # Send email
        success, message = pdf_generator.send_email(
            email_address=email_address,
            subject=subject,
            body=body,
            custom_message=custom_message
        )
        
        if success:
            messages.success(request, _('PDF sent to %(email)s successfully!') % {'email': email_address})
        else:
            messages.error(request, _('Failed to send email: %(error)s') % {'error': message})
        
        return redirect('medical:consultation_detail', pk=pk)

Step 4: Add URL Routes

In the module's urls.py:

urlpatterns = [
    # ... existing routes ...
    path('<uuid:pk>/pdf/', views.MedicalConsultationPDFView.as_view(), name='consultation_pdf'),
    path('<uuid:pk>/email-pdf/', views.MedicalConsultationEmailPDFView.as_view(), name='consultation_email_pdf'),
]

Step 5: Update Detail Template

Add PDF options dropdown to the detail template:

<div class="btn-group">
    <button type="button" class="btn btn-outline-secondary dropdown-toggle" data-bs-toggle="dropdown">
        <i class="fas fa-file-pdf me-1"></i>{% trans "PDF Options" %}
    </button>
    <ul class="dropdown-menu">
        <li>
            <a class="dropdown-item" href="{% url 'medical:consultation_pdf' consultation.pk %}?view=inline" target="_blank">
                <i class="fas fa-eye me-2"></i>{% trans "View PDF" %}
            </a>
        </li>
        <li>
            <a class="dropdown-item" href="{% url 'medical:consultation_pdf' consultation.pk %}" target="_blank">
                <i class="fas fa-download me-2"></i>{% trans "Download PDF" %}
            </a>
        </li>
        <li><hr class="dropdown-divider"></li>
        <li>
            <a class="dropdown-item" href="#" data-bs-toggle="modal" data-bs-target="#emailPdfModal">
                <i class="fas fa-envelope me-2"></i>{% trans "Email PDF to Patient" %}
            </a>
        </li>
    </ul>
</div>

<!-- Email PDF Modal -->
<div class="modal fade" id="emailPdfModal" tabindex="-1">
    <div class="modal-dialog">
        <div class="modal-content">
            <form method="post" action="{% url 'medical:consultation_email_pdf' consultation.pk %}">
                {% csrf_token %}
                <div class="modal-header">
                    <h5 class="modal-title">
                        <i class="fas fa-envelope me-2"></i>{% trans "Email PDF to Patient" %}
                    </h5>
                    <button type="button" class="btn-close" data-bs-dismiss="modal"></button>
                </div>
                <div class="modal-body">
                    <div class="mb-3">
                        <label for="email_address" class="form-label">{% trans "Email Address" %}</label>
                        <input type="email" name="email_address" id="email_address" class="form-control" 
                               value="{{ consultation.patient.email }}" required>
                    </div>
                    <div class="mb-3">
                        <label for="email_message" class="form-label">{% trans "Additional Message (Optional)" %}</label>
                        <textarea name="email_message" id="email_message" class="form-control" rows="3"></textarea>
                    </div>
                </div>
                <div class="modal-footer">
                    <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">{% trans "Close" %}</button>
                    <button type="submit" class="btn btn-primary">
                        <i class="fas fa-paper-plane me-1"></i>{% trans "Send Email" %}
                    </button>
                </div>
            </form>
        </div>
    </div>
</div>

Modules to Implement

Priority 1 (Week 1)

  1. Appointments - Already implemented
  2. Finance/Invoices - Already implemented
  3. Medical Consultation (MD-F-1)
  4. Medical Follow-up (MD-F-2)
  5. Nursing Encounter

Priority 2 (Week 2)

  1. OT Consultation (OT-F-1)
  2. OT Session (OT-F-3)
  3. SLP Consultation (SLP-F-1)

Priority 3 (Week 3)

  1. SLP Assessment (SLP-F-2)
  2. SLP Intervention (SLP-F-3)
  3. ABA Consult (ABA-F-1)

Priority 4 (Week 4)

  1. Consent Forms
  2. Patient Summary

Tips for Implementation

  1. Keep sections modular: Each clinical form has different sections, so structure them clearly in get_document_sections()

  2. Handle optional fields: Use or 'N/A' for fields that might be empty

  3. Format dates consistently: Use strftime('%Y-%m-%d') for dates

  4. Test Arabic text: Make sure Arabic names and content display correctly

  5. Check permissions: Ensure only authorized users can generate/email PDFs

  6. Validate email addresses: Always validate before sending emails

  7. Handle errors gracefully: Wrap email sending in try-except blocks

Testing Checklist

For each implementation, test:

  • PDF generates without errors
  • Tenant logo displays correctly
  • Arabic text renders properly
  • All sections display complete data
  • View inline opens in browser
  • Download saves file correctly
  • Email sends successfully
  • Email contains correct PDF attachment
  • Custom message appears in email
  • Permissions are enforced

Next Steps

  1. Start with Medical Consultation (most used)
  2. Test thoroughly
  3. Use as template for other modules
  4. Iterate and improve based on feedback