File size: 5,135 Bytes
dd784bb
5ed5b69
 
ab81fa8
5ed5b69
 
dd784bb
 
 
 
 
 
 
 
 
 
 
5ed5b69
dd784bb
 
ab81fa8
5ed5b69
 
dd784bb
 
 
 
ab81fa8
5ed5b69
 
dd784bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ed5b69
 
 
dd784bb
 
 
5ed5b69
dd784bb
 
 
 
 
5ed5b69
 
dd784bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5ed5b69
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# /home/user/app/services/pdf_report.py
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import inch
from io import BytesIO
from typing import List, Optional # Optional for tool_name
from pathlib import Path

# Assuming ChatMessage is defined in models.py or models/chat.py
# Make sure ChatMessage has 'role', 'content', and optionally 'tool_name' attributes.
# Example ChatMessage structure (Pydantic or dataclass):
# class ChatMessage(BaseModel):
#     role: str  # "user", "assistant", "tool"
#     content: str
#     tool_name: Optional[str] = None
from models import ChatMessage # Or from models.chat import ChatMessage
from config.settings import settings # For APP_TITLE
from assets.logo import get_logo_path # Corrected import path
from services.logger import app_logger # For logging issues

def generate_pdf_report(chat_messages: List[ChatMessage], patient_name: str = "Patient") -> BytesIO:
    buffer = BytesIO()
    # Reduce margins for more content space
    doc = SimpleDocTemplate(buffer, pagesize=letter,
                            leftMargin=0.75*inch, rightMargin=0.75*inch,
                            topMargin=0.75*inch, bottomMargin=0.75*inch)
    styles = getSampleStyleSheet()
    story = []

    # Style adjustments
    styles['h1'].alignment = 1 # Center align H1
    styles['Normal'].spaceBefore = 6
    styles['Normal'].spaceAfter = 6
    code_style = styles['Code'] # For AI and Tool messages
    code_style.spaceBefore = 6
    code_style.spaceAfter = 6
    code_style.leftIndent = 10 # Indent AI/Tool messages slightly

    # 1. Logo (optional)
    logo_path_str = get_logo_path()
    if logo_path_str:
        logo_file = Path(logo_path_str) # Convert to Path object
        if logo_file.exists():
            try:
                # Adjust width/height as needed, preserve aspect ratio if possible
                img = Image(logo_file, width=1.0*inch, height=1.0*inch, preserveAspectRatio=True)
                img.hAlign = 'LEFT' # Align logo to the left
                story.append(img)
                story.append(Spacer(1, 0.2*inch))
            except Exception as e:
                app_logger.warning(f"Could not add logo to PDF from path '{logo_path_str}': {e}")
        else:
            app_logger.warning(f"Logo path '{logo_path_str}' from get_logo_path() does not exist.")

    # 2. Title
    title_text = f"{settings.APP_TITLE} - Consultation Report"
    title = Paragraph(title_text, styles['h1'])
    story.append(title)
    story.append(Spacer(1, 0.2*inch))

    # 3. Patient Info
    patient_info_text = f"<b>Patient:</b> {patient_name}" # Make "Patient:" bold
    patient_info = Paragraph(patient_info_text, styles['h2'])
    story.append(patient_info)
    story.append(Spacer(1, 0.3*inch)) # More space after patient info

    # 4. Chat Transcript
    story.append(Paragraph("<u>Consultation Transcript:</u>", styles['h3'])) # Underline transcript title
    story.append(Spacer(1, 0.1*inch))

    for msg in chat_messages:
        prefix = ""
        current_style = styles['Normal']

        if msg.role == 'assistant':
            prefix = "<b>AI Assistant:</b> "
            current_style = code_style
        elif msg.role == 'user':
            prefix = "<b>You:</b> "
            current_style = styles['Normal']
        elif msg.role == 'tool':
            tool_name = getattr(msg, 'tool_name', 'UnknownTool') # Handle if tool_name is missing
            prefix = f"<b>Tool ({tool_name}):</b> "
            current_style = code_style
        else:
            prefix = f"<b>{msg.role.capitalize()}:</b> " # Fallback for other roles

        # Sanitize content for ReportLab Paragraph (handles HTML-like tags)
        # Replace newlines with <br/> for PDF line breaks
        content = msg.content.replace('\n', '<br/>\n')
        # Escape < and > that are not part of <br/>
        content = content.replace("<", "<").replace(">", ">").replace("<br/>", "<br/>")

        try:
            story.append(Paragraph(prefix + content, current_style))
            story.append(Spacer(1, 0.05*inch)) # Smaller spacer between messages
        except Exception as e:
            app_logger.error(f"Error adding message to PDF: {prefix}{content}. Error: {e}")
            story.append(Paragraph(f"<i>Error rendering message: {e}</i>", styles['Italic']))


    try:
        doc.build(story)
        buffer.seek(0)
        app_logger.info(f"PDF report generated successfully for patient: {patient_name}")
    except Exception as e:
        app_logger.error(f"Failed to build PDF document: {e}", exc_info=True)
        # Return an empty or error buffer if build fails
        buffer = BytesIO() # Reset buffer
        error_doc = SimpleDocTemplate(buffer, pagesize=letter)
        error_story = [Paragraph("Error generating PDF report. Please check logs.", styles['h1'])]
        try:
            error_doc.build(error_story)
        except: pass # If even error doc fails, just return empty buffer
        buffer.seek(0)

    return buffer