File size: 6,461 Bytes
9105915 1ce8e69 9105915 1806672 b8415d6 2acbd28 de16828 f409157 1ce8e69 9105915 1ce8e69 2acbd28 1ce8e69 b8415d6 9105915 b8415d6 9105915 b8415d6 1ce8e69 de16828 1806672 9105915 1806672 9105915 1806672 9105915 de16828 1806672 2acbd28 f409157 2acbd28 f409157 2acbd28 f409157 2acbd28 f409157 2acbd28 f409157 2acbd28 f409157 2acbd28 1ce8e69 |
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 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
# import io
# from reportlab.lib.pagesizes import letter
# from reportlab.platypus import SimpleDocTemplate, Image, Paragraph, Spacer
# from reportlab.lib.styles import getSampleStyleSheet
# import matplotlib.pyplot as plt
# class ReportGenerator:
# def __init__(self):
# self.styles = getSampleStyleSheet()
# def create_combined_pdf(self, intervention_fig, student_metrics_fig, recommendations):
# buffer = io.BytesIO()
# doc = SimpleDocTemplate(buffer, pagesize=letter)
# elements = []
# # Add the intervention statistics chart
# elements.extend(self._add_chart(intervention_fig, "Intervention Statistics"))
# # Add the student metrics chart
# elements.extend(self._add_chart(student_metrics_fig, "Student Metrics"))
# # Add the AI recommendations
# elements.extend(self._add_recommendations(recommendations))
# # Build the PDF
# doc.build(elements)
# buffer.seek(0)
# return buffer
# def _add_chart(self, fig, title):
# elements = []
# elements.append(Paragraph(title, self.styles['Heading2']))
# img_buffer = io.BytesIO()
# if hasattr(fig, 'write_image'): # Plotly figure
# fig.write_image(img_buffer, format="png")
# elif isinstance(fig, plt.Figure): # Matplotlib figure
# fig.savefig(img_buffer, format='png')
# plt.close(fig) # Close the figure to free up memory
# else:
# raise ValueError(f"Unsupported figure type: {type(fig)}")
# img_buffer.seek(0)
# elements.append(Image(img_buffer, width=500, height=300))
# elements.append(Spacer(1, 12))
# return elements
# def _add_recommendations(self, recommendations):
# elements = []
# elements.append(Paragraph("AI Recommendations", self.styles['Heading1']))
# elements.append(Paragraph(recommendations, self.styles['BodyText']))
# return elements
import io
from reportlab.lib.pagesizes import letter
from reportlab.platypus import SimpleDocTemplate, Image, Paragraph, Spacer
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_JUSTIFY
from reportlab.lib.units import inch
import matplotlib.pyplot as plt
import markdown
from xml.etree import ElementTree as ET
from PIL import Image as PILImage
from xml.parsers.expat import ExpatError
from html import escape
class ReportGenerator:
def __init__(self):
self.styles = getSampleStyleSheet()
self.styles.add(ParagraphStyle(name='Justify', alignment=TA_JUSTIFY))
def create_combined_pdf(self, intervention_fig, student_metrics_fig, recommendations):
buffer = io.BytesIO()
doc = SimpleDocTemplate(buffer, pagesize=letter)
elements = []
elements.extend(self._add_chart(intervention_fig, "Intervention Statistics"))
elements.extend(self._add_chart(student_metrics_fig, "Student Metrics"))
elements.extend(self._add_recommendations(recommendations))
doc.build(elements)
buffer.seek(0)
return buffer
def _add_chart(self, fig, title):
elements = []
elements.append(Paragraph(title, self.styles['Heading2']))
img_buffer = io.BytesIO()
if hasattr(fig, 'write_image'): # Plotly figure
fig.write_image(img_buffer, format="png", width=700, height=400)
elif isinstance(fig, plt.Figure): # Matplotlib figure
fig.set_size_inches(10, 6) # Set a consistent size
fig.savefig(img_buffer, format='png', dpi=100, bbox_inches='tight')
plt.close(fig)
else:
raise ValueError(f"Unsupported figure type: {type(fig)}")
img_buffer.seek(0)
# Use PIL to get image dimensions
with PILImage.open(img_buffer) as img:
img_width, img_height = img.size
# Calculate width and height to maintain aspect ratio
max_width = 6.5 * inch # Maximum width (letter width is 8.5 inches, leaving margins)
max_height = 4 * inch # Maximum height
aspect = img_width / float(img_height)
if img_width > max_width:
img_width = max_width
img_height = img_width / aspect
if img_height > max_height:
img_height = max_height
img_width = img_height * aspect
# Reset buffer position
img_buffer.seek(0)
# Create ReportLab Image with calculated dimensions
img = Image(img_buffer, width=img_width, height=img_height)
elements.append(img)
elements.append(Spacer(1, 12))
return elements
def _add_recommendations(self, recommendations):
elements = []
elements.append(Paragraph("AI Recommendations", self.styles['Heading1']))
# Convert markdown to HTML
html = markdown.markdown(recommendations)
# Wrap the HTML in a root element to ensure valid XML
wrapped_html = f"<root>{html}</root>"
try:
root = ET.fromstring(wrapped_html)
except ExpatError:
# If parsing fails, fallback to treating the entire content as plain text
elements.append(Paragraph(escape(recommendations), self.styles['BodyText']))
return elements
for elem in root:
if elem.tag == 'h3':
elements.append(Paragraph(elem.text or "", self.styles['Heading3']))
elif elem.tag == 'h4':
elements.append(Paragraph(elem.text or "", self.styles['Heading4']))
elif elem.tag == 'p':
text = ''.join(elem.itertext())
elements.append(Paragraph(text, self.styles['Justify']))
elif elem.tag == 'ul':
for li in elem.findall('li'):
bullet_text = '• ' + ''.join(li.itertext()).strip()
elements.append(Paragraph(bullet_text, self.styles['BodyText']))
else:
# For any other tags, just extract the text
text = ''.join(elem.itertext())
if text.strip():
elements.append(Paragraph(text, self.styles['BodyText']))
return elements |