281 lines
10 KiB
Python
281 lines
10 KiB
Python
"""
|
|
PDF generation service for acknowledgements
|
|
"""
|
|
from io import BytesIO
|
|
from datetime import datetime
|
|
|
|
from django.conf import settings
|
|
from django.utils import translation
|
|
from reportlab.lib import colors
|
|
from reportlab.lib.pagesizes import letter, A4
|
|
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
|
|
from reportlab.lib.units import inch
|
|
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, Paragraph, Spacer, Image
|
|
from reportlab.lib.enums import TA_LEFT, TA_CENTER, TA_RIGHT
|
|
|
|
|
|
class AcknowledgementPDFService:
|
|
"""Service for generating PDF documents for acknowledgements"""
|
|
|
|
@staticmethod
|
|
def generate_pdf(user, acknowledgement, language='en'):
|
|
"""
|
|
Generate PDF for a user acknowledgement
|
|
|
|
Args:
|
|
user: User instance
|
|
acknowledgement: UserAcknowledgement instance
|
|
language: Language code ('en' or 'ar')
|
|
|
|
Returns:
|
|
BytesIO object containing PDF data
|
|
"""
|
|
# Set language for translation
|
|
translation.activate(language)
|
|
|
|
# Create buffer
|
|
buffer = BytesIO()
|
|
|
|
# Create PDF document
|
|
doc = SimpleDocTemplate(
|
|
buffer,
|
|
pagesize=A4,
|
|
rightMargin=72,
|
|
leftMargin=72,
|
|
topMargin=72,
|
|
bottomMargin=18
|
|
)
|
|
|
|
# Build content
|
|
story = []
|
|
styles = getSampleStyleSheet()
|
|
|
|
# Custom styles
|
|
title_style = ParagraphStyle(
|
|
'CustomTitle',
|
|
parent=styles['Heading1'],
|
|
fontSize=24,
|
|
textColor=colors.HexColor('#2c3e50'),
|
|
spaceAfter=30,
|
|
alignment=TA_CENTER
|
|
)
|
|
|
|
subtitle_style = ParagraphStyle(
|
|
'CustomSubtitle',
|
|
parent=styles['Heading2'],
|
|
fontSize=16,
|
|
textColor=colors.HexColor('#3498db'),
|
|
spaceAfter=20
|
|
)
|
|
|
|
body_style = ParagraphStyle(
|
|
'CustomBody',
|
|
parent=styles['BodyText'],
|
|
fontSize=11,
|
|
spaceAfter=12,
|
|
leading=16
|
|
)
|
|
|
|
heading_style = ParagraphStyle(
|
|
'CustomHeading',
|
|
parent=styles['Heading3'],
|
|
fontSize=14,
|
|
textColor=colors.HexColor('#2c3e50'),
|
|
spaceAfter=10,
|
|
spaceBefore=20
|
|
)
|
|
|
|
# Title
|
|
if language == 'ar':
|
|
title = "إقرار الاستلام والتوقيع"
|
|
else:
|
|
title = "Acknowledgement Receipt"
|
|
|
|
story.append(Paragraph(title, title_style))
|
|
story.append(Spacer(1, 0.2*inch))
|
|
|
|
# Header line
|
|
header_data = [
|
|
('Date', 'التاريخ'),
|
|
('Employee Name', 'اسم الموظف'),
|
|
('Employee ID', 'رقم الموظف'),
|
|
('Email', 'البريد الإلكتروني'),
|
|
]
|
|
|
|
user_data = [
|
|
(acknowledgement.acknowledged_at.strftime('%Y-%m-%d %H:%M:%S')),
|
|
(user.get_full_name()),
|
|
(user.employee_id or 'N/A'),
|
|
(user.email),
|
|
]
|
|
|
|
# Create header table
|
|
header_table_data = []
|
|
for i, (label_en, label_ar) in enumerate(header_data):
|
|
if language == 'ar':
|
|
header_table_data.append([
|
|
Paragraph(f"<b>{label_ar}:</b>", body_style),
|
|
Paragraph(user_data[i], body_style)
|
|
])
|
|
else:
|
|
header_table_data.append([
|
|
Paragraph(f"<b>{label_en}:</b>", body_style),
|
|
Paragraph(user_data[i], body_style)
|
|
])
|
|
|
|
header_table = Table(header_table_data, colWidths=[2*inch, 3*inch])
|
|
header_table.setStyle(TableStyle([
|
|
('BACKGROUND', (0, 0), (0, -1), colors.HexColor('#f8f9fa')),
|
|
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
|
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
|
|
('TOPPADDING', (0, 0), (-1, -1), 8),
|
|
('BOTTOMPADDING', (0, 0), (-1, -1), 8),
|
|
]))
|
|
|
|
story.append(header_table)
|
|
story.append(Spacer(1, 0.3*inch))
|
|
|
|
# Acknowledgement details
|
|
story.append(Paragraph(
|
|
"Acknowledgement Details" if language == 'en' else "تفاصيل الإقرار",
|
|
subtitle_style
|
|
))
|
|
|
|
# Get checklist item text based on language
|
|
checklist_item = acknowledgement.checklist_item
|
|
if language == 'ar' and checklist_item.text_ar:
|
|
item_text = checklist_item.text_ar
|
|
else:
|
|
item_text = checklist_item.text_en
|
|
|
|
if language == 'ar' and checklist_item.description_ar:
|
|
item_desc = checklist_item.description_ar
|
|
else:
|
|
item_desc = checklist_item.description_en or checklist_item.description_en
|
|
|
|
# Item details
|
|
item_details = [
|
|
('Acknowledgement Code', 'رمز الإقرار', checklist_item.code),
|
|
('Acknowledgement Item', 'بند الإقرار', item_text),
|
|
]
|
|
|
|
if item_desc:
|
|
item_details.append(('Description', 'الوصف', item_desc))
|
|
|
|
if checklist_item.content:
|
|
if language == 'ar' and checklist_item.content.title_ar:
|
|
content_title = checklist_item.content.title_ar
|
|
else:
|
|
content_title = checklist_item.content.title_en
|
|
item_details.append(('Section', 'القسم', content_title))
|
|
|
|
# Create item table
|
|
item_table_data = []
|
|
for label_en, label_ar, value in item_details:
|
|
if language == 'ar':
|
|
item_table_data.append([
|
|
Paragraph(f"<b>{label_ar}:</b>", body_style),
|
|
Paragraph(value, body_style)
|
|
])
|
|
else:
|
|
item_table_data.append([
|
|
Paragraph(f"<b>{label_en}:</b>", body_style),
|
|
Paragraph(value, body_style)
|
|
])
|
|
|
|
item_table = Table(item_table_data, colWidths=[2*inch, 4*inch])
|
|
item_table.setStyle(TableStyle([
|
|
('BACKGROUND', (0, 0), (0, -1), colors.HexColor('#e8f4f8')),
|
|
('VALIGN', (0, 0), (-1, -1), 'TOP'),
|
|
('GRID', (0, 0), (-1, -1), 0.5, colors.grey),
|
|
('TOPPADDING', (0, 0), (-1, -1), 8),
|
|
('BOTTOMPADDING', (0, 0), (-1, -1), 8),
|
|
]))
|
|
|
|
story.append(item_table)
|
|
story.append(Spacer(1, 0.3*inch))
|
|
|
|
# Signature section
|
|
story.append(Paragraph(
|
|
"Digital Signature" if language == 'en' else "التوقيع الرقمي",
|
|
heading_style
|
|
))
|
|
|
|
signature_text = (
|
|
"This acknowledgement was digitally signed by the employee above."
|
|
if language == 'en'
|
|
else "تم توقيع هذا الإقرار رقميًا بواسطة الموظف المذكور أعلاه."
|
|
)
|
|
|
|
story.append(Paragraph(signature_text, body_style))
|
|
story.append(Spacer(1, 0.1*inch))
|
|
|
|
# IP and User Agent
|
|
if acknowledgement.signature_ip:
|
|
ip_text = f"IP Address: {acknowledgement.signature_ip}" if language == 'en' else f"عنوان IP: {acknowledgement.signature_ip}"
|
|
story.append(Paragraph(f"<b>{ip_text}</b>", body_style))
|
|
|
|
if acknowledgement.signature_user_agent:
|
|
ua_text = f"Device: {acknowledgement.signature_user_agent[:100]}..." if language == 'en' else f"الجهاز: {acknowledgement.signature_user_agent[:100]}..."
|
|
story.append(Paragraph(f"<b>{ua_text}</b>", body_style))
|
|
|
|
story.append(Spacer(1, 0.5*inch))
|
|
|
|
# Declaration
|
|
declaration_text_en = (
|
|
"I hereby acknowledge that I have read and understood the above acknowledgement "
|
|
"and agree to comply with all policies and procedures outlined therein. "
|
|
"I understand that this is a legally binding document."
|
|
)
|
|
declaration_text_ar = (
|
|
"أقر هنا بأنني قرأت وفهمت الإقرار المذكور أعلاه وأوافق على الامتثال لجميع "
|
|
"السياسات والإجراءات المنصوص عليها فيه. أفهم أن هذا وثيقة ملزمة قانونيًا."
|
|
)
|
|
|
|
declaration = declaration_text_ar if language == 'ar' else declaration_text_en
|
|
story.append(Paragraph(f"<i>{declaration}</i>", body_style))
|
|
|
|
story.append(Spacer(1, 1.5*inch))
|
|
|
|
# Signature line
|
|
signature_label = "Digital Signature" if language == 'en' else "التوقيع الرقمي"
|
|
story.append(Paragraph(f"<b>{signature_label}</b>", body_style))
|
|
story.append(Spacer(1, 0.2*inch))
|
|
|
|
# Add signature image if available
|
|
if acknowledgement.signature:
|
|
try:
|
|
from base64 import b64decode
|
|
sig_data = b64decode(acknowledgement.signature)
|
|
sig_buffer = BytesIO(sig_data)
|
|
sig_image = Image(sig_buffer, width=2*inch, height=1*inch)
|
|
story.append(sig_image)
|
|
except:
|
|
pass
|
|
|
|
# Footer
|
|
story.append(Spacer(1, 0.5*inch))
|
|
footer_text = (
|
|
f"Generated by PX360 Patient Experience Management System on {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
|
if language == 'en'
|
|
else f"تم الإنشاء بواسطة نظام إدارة تجربة المريض PX360 في {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
|
)
|
|
|
|
footer_style = ParagraphStyle(
|
|
'CustomFooter',
|
|
parent=styles['BodyText'],
|
|
fontSize=9,
|
|
textColor=colors.grey,
|
|
alignment=TA_CENTER
|
|
)
|
|
|
|
story.append(Paragraph(footer_text, footer_style))
|
|
|
|
# Build PDF
|
|
doc.build(story)
|
|
|
|
# Get PDF value
|
|
pdf_value = buffer.getvalue()
|
|
buffer.close()
|
|
|
|
return pdf_value |