feat: Miniaturas en preguntas y galería completa en PDF - backend v1.0.14

- Miniaturas pequeñas (1x0.8 inch) en cada pregunta (4 por fila)
- Nueva página con galería completa al final del PDF
- Imágenes más grandes en galería (2.5x2 inch, 2 por fila)
- Captions con sección y pregunta en galería
- Mejor distribución del espacio en el PDF
This commit is contained in:
2025-11-21 17:38:52 -03:00
parent ac7b582d8a
commit 3100473eb6
2 changed files with 92 additions and 9 deletions

View File

@@ -1802,12 +1802,12 @@ def export_inspection_to_pdf(
elements.append(question_table) elements.append(question_table)
elements.append(Spacer(1, 5)) elements.append(Spacer(1, 5))
# Fotos adjuntas # Fotos adjuntas - Miniaturas pequeñas
if answer.media_files and len(answer.media_files) > 0: if answer.media_files and len(answer.media_files) > 0:
elements.append(Spacer(1, 5)) elements.append(Spacer(1, 3))
photos_per_row = 2 photos_per_row = 4 # Más fotos por fila (miniaturas)
photo_width = 2.5 * inch thumbnail_width = 1 * inch # Miniaturas pequeñas
photo_height = 2 * inch thumbnail_height = 0.8 * inch
for i in range(0, len(answer.media_files), photos_per_row): for i in range(0, len(answer.media_files), photos_per_row):
photo_row = [] photo_row = []
@@ -1818,12 +1818,12 @@ def export_inspection_to_pdf(
if photo_path.startswith('data:image'): if photo_path.startswith('data:image'):
img_data = base64.b64decode(photo_path.split(',')[1]) img_data = base64.b64decode(photo_path.split(',')[1])
img_buffer = BytesIO(img_data) img_buffer = BytesIO(img_data)
img = RLImage(img_buffer, width=photo_width, height=photo_height) img = RLImage(img_buffer, width=thumbnail_width, height=thumbnail_height)
else: else:
# Si es una ruta de archivo # Si es una ruta de archivo
full_path = os.path.join(os.getcwd(), photo_path) full_path = os.path.join(os.getcwd(), photo_path)
if os.path.exists(full_path): if os.path.exists(full_path):
img = RLImage(full_path, width=photo_width, height=photo_height) img = RLImage(full_path, width=thumbnail_width, height=thumbnail_height)
else: else:
continue continue
photo_row.append(img) photo_row.append(img)
@@ -1836,13 +1836,96 @@ def export_inspection_to_pdf(
photo_table.setStyle(TableStyle([ photo_table.setStyle(TableStyle([
('ALIGN', (0, 0), (-1, -1), 'CENTER'), ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'), ('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('LEFTPADDING', (0, 0), (-1, -1), 2),
('RIGHTPADDING', (0, 0), (-1, -1), 2),
])) ]))
elements.append(photo_table) elements.append(photo_table)
elements.append(Spacer(1, 10)) elements.append(Spacer(1, 5))
else: else:
elements.append(Paragraph("No hay respuestas registradas", styles['Normal'])) elements.append(Paragraph("No hay respuestas registradas", styles['Normal']))
# GALERÍA COMPLETA DE FOTOS AL FINAL
all_photos = []
for answer in answers:
if answer.media_files:
for media_file in answer.media_files:
all_photos.append({
'file': media_file,
'question': answer.question.text,
'section': answer.question.section
})
if all_photos:
# Nueva página para galería
elements.append(PageBreak())
elements.append(Paragraph("GALERÍA COMPLETA DE IMÁGENES", heading_style))
elements.append(Spacer(1, 20))
photos_per_row = 2 # Imágenes más grandes en galería
photo_width = 2.5 * inch
photo_height = 2 * inch
for i in range(0, len(all_photos), photos_per_row):
photo_row = []
caption_row = []
for photo_data in all_photos[i:i+photos_per_row]:
try:
photo_path = photo_data['file'].file_path
# Si la foto es base64
if photo_path.startswith('data:image'):
img_data = base64.b64decode(photo_path.split(',')[1])
img_buffer = BytesIO(img_data)
img = RLImage(img_buffer, width=photo_width, height=photo_height)
else:
# Si es una ruta de archivo
full_path = os.path.join(os.getcwd(), photo_path)
if os.path.exists(full_path):
img = RLImage(full_path, width=photo_width, height=photo_height)
else:
continue
photo_row.append(img)
# Crear caption con sección y pregunta
caption_style = ParagraphStyle(
'Caption',
parent=styles['Normal'],
fontSize=7,
alignment=TA_CENTER,
textColor=colors.HexColor('#6b7280')
)
caption_text = f"<b>{photo_data['section']}</b><br/>{photo_data['question'][:60]}{'...' if len(photo_data['question']) > 60 else ''}"
caption_row.append(Paragraph(caption_text, caption_style))
except Exception as e:
print(f"Error loading gallery image: {e}")
continue
if photo_row:
# Tabla de fotos
photo_table = Table([photo_row], colWidths=[photo_width] * len(photo_row))
photo_table.setStyle(TableStyle([
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('VALIGN', (0, 0), (-1, -1), 'MIDDLE'),
('LEFTPADDING', (0, 0), (-1, -1), 5),
('RIGHTPADDING', (0, 0), (-1, -1), 5),
]))
elements.append(photo_table)
# Tabla de captions
caption_table = Table([caption_row], colWidths=[photo_width] * len(caption_row))
caption_table.setStyle(TableStyle([
('ALIGN', (0, 0), (-1, -1), 'CENTER'),
('VALIGN', (0, 0), (-1, -1), 'TOP'),
('LEFTPADDING', (0, 0), (-1, -1), 5),
('RIGHTPADDING', (0, 0), (-1, -1), 5),
('TOPPADDING', (0, 0), (-1, -1), 2),
]))
elements.append(caption_table)
elements.append(Spacer(1, 15))
# Pie de página # Pie de página
elements.append(Spacer(1, 30)) elements.append(Spacer(1, 30))
footer_style = ParagraphStyle( footer_style = ParagraphStyle(

View File

@@ -20,7 +20,7 @@ services:
retries: 5 retries: 5
backend: backend:
image: dymai/syntria-backend:1.0.13 image: dymai/syntria-backend:1.0.14
container_name: syntria-backend-prod container_name: syntria-backend-prod
restart: always restart: always
depends_on: depends_on: