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:
@@ -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(
|
||||||
|
|||||||
@@ -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:
|
||||||
|
|||||||
Reference in New Issue
Block a user