draft-estimation / services /pdf_service.py
PauloFN's picture
first
6a6918c
raw
history blame
5.52 kB
from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import letter
from reportlab.lib.utils import ImageReader
from io import BytesIO
from models.schemas import MeasurementMetadata, MeasurementResult
from PIL import Image, ImageDraw
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
class PdfService:
"""
A service to generate PDF reports.
"""
def create_report(
self,
image_bytes: bytes,
metadata: MeasurementMetadata,
results: MeasurementResult,
ml_results: dict
) -> bytes:
"""
Generates a PDF report with the measurement data.
"""
logger.info(f"Creating PDF with metadata: {metadata}")
logger.info(f"Creating PDF with results: {results}")
buffer = BytesIO()
p = canvas.Canvas(buffer, pagesize=letter)
width, height = letter
# Title
p.setFont("Helvetica-Bold", 16)
p.drawString(72, height - 72, "Ship Draft Measurement Report")
# Metadata
p.setFont("Helvetica", 12)
p.drawString(72, height - 108, f"Ship ID: {metadata.ship_id}")
p.drawString(72, height - 126, f"Timestamp: {metadata.timestamp.strftime('%Y-%m-%d %H:%M:%S')}")
p.drawString(72, height - 144, f"Latitude: {metadata.latitude}")
p.drawString(72, height - 162, f"Longitude: {metadata.longitude}")
# # Measurement Results
# p.setFont("Helvetica-Bold", 14)
# p.drawString(72, height - 198, "Measurement Results")
# p.setFont("Helvetica", 12)
# p.drawString(90, height - 218, f"Draft Measurement: {results.draft_measurement:.1f} meters")
# p.drawString(90, height - 236, f"Confidence Score: {results.confidence_score:.2%}")
# ML Results
p.setFont("Helvetica-Bold", 14)
y_position = height - 270
p.drawString(72, y_position, "ML Results")
y_position -= 18
p.setFont("Helvetica", 12)
# Highlight draft from ML results
if 'draft' in ml_results:
p.setFont("Helvetica-Bold", 12) # Highlight
p.drawString(90, y_position, f"ML Draft: {ml_results['draft']:.2f} meters")
p.setFont("Helvetica", 12) # Reset font
y_position -= 18
# Add other ML results (excluding arrays and images)
for key, value in ml_results.items():
if key in ['pose_results', 'segment_results', 'original_image', 'pose_image_result', 'segment_image_result', 'final_image_result']:
continue
p.drawString(90, y_position, f"{key.replace('_', ' ').title()}: {value}")
y_position -= 18
# Images
p.setFont("Helvetica-Bold", 14)
y_position -= 18
p.drawString(72, y_position, "Images")
y_position -= 18
p.setFont("Helvetica", 12)
# Convert numpy arrays to PIL Image and then to bytes for ReportLab
def get_image_bytes(np_array):
if np_array is None:
return None
img = Image.fromarray(np_array.astype('uint8'))
img_byte_arr = BytesIO()
img.save(img_byte_arr, format='PNG')
return img_byte_arr.getvalue()
# Define starting positions and dimensions for horizontal layout
x_start = 72
image_width = 150
image_height = 150 # Assuming square or adjust as needed
y_image_row = y_position - image_height - 20 # Position for the bottom of the images
current_x = x_start
# Draw original image
if 'original_image' in ml_results and ml_results['original_image'] is not None:
original_img_bytes = get_image_bytes(ml_results['original_image'])
if original_img_bytes:
p.drawString(current_x, y_image_row + image_height + 5, "Original Image:") # Label above image
p.drawImage(ImageReader(BytesIO(original_img_bytes)), current_x, y_image_row, width=image_width, height=image_height, preserveAspectRatio=True)
current_x += image_width + 20 # Move x for next image
# Draw pose image result
if 'pose_image_result' in ml_results and ml_results['pose_image_result'] is not None:
pose_img_bytes = get_image_bytes(ml_results['pose_image_result'])
if pose_img_bytes:
p.drawString(current_x, y_image_row + image_height + 5, "Pose Image Result:")
p.drawImage(ImageReader(BytesIO(pose_img_bytes)), current_x, y_image_row, width=image_width, height=image_height, preserveAspectRatio=True)
current_x += image_width + 20
# Draw segment image result
if 'segment_image_result' in ml_results and ml_results['segment_image_result'] is not None:
segment_img_bytes = get_image_bytes(ml_results['segment_image_result'])
if segment_img_bytes:
p.drawString(current_x, y_image_row + image_height + 5, "Segment Image Result:")
p.drawImage(ImageReader(BytesIO(segment_img_bytes)), current_x, y_image_row, width=image_width, height=image_height, preserveAspectRatio=True)
# No need to update current_x as it's the last image in the row
# Update y_position for content after images
y_position = y_image_row - 20 # Adjust y_position to be below the images
p.showPage()
p.save()
pdf_bytes = buffer.getvalue()
buffer.close()
return pdf_bytes