Spaces:
Sleeping
Sleeping
import gradio as gr | |
from fpdf import FPDF | |
from PIL import Image | |
from groq import Groq | |
from datetime import datetime, timezone | |
import base64 | |
from io import BytesIO | |
import time, os | |
max_texts_rows = 5 | |
max_texts_elements = 10 | |
max_img_desc_rows = 5 | |
init_texts_rows_state = 1 | |
init_img_desc_rows_state = 0 | |
custom_css = """ | |
html { | |
height: 100%; | |
margin: 0; | |
display: flex; | |
justify-content: center; | |
align-items: center; | |
} | |
body { | |
min-height: 50vh; | |
aspect-ratio: 1 / 1.414; /* A4 aspect ratio */ | |
width: 100%; /* Full width */ | |
max-width: 210mm; /* Max width for A4 */ | |
max-height: 297mm; /* Max height for A4 */ | |
padding: 10px; /* Optional padding */ | |
box-sizing: border-box; | |
margin: 0 auto; /* Center the block */ | |
overflow: auto; /* Ensure content is scrollable if it exceeds the block size */ | |
gap: 10px; /* Add gap between elements */ | |
border: 1px solid #000; | |
} | |
.gradio-container{ | |
background-color: white; | |
} | |
#logo-upload { | |
min-height: 100px; | |
max-height: 100px; | |
} | |
#text-empresa { | |
min-height: 100px; | |
max-height: 100px; | |
} | |
.text-box { | |
} | |
.image-upload{ | |
min-height: 300px; | |
max-height: 300px; | |
} | |
img { | |
object-fit: contain; | |
} | |
.description { | |
min-height: 300px; | |
max-height: 300px; | |
} | |
h1, h2, h3 { | |
color: rgb(60, 60, 60); | |
} | |
""" | |
api_key = os.getenv("GROQ_API_KEY") | |
client = Groq( | |
api_key=api_key, | |
) | |
with gr.Blocks(css=custom_css) as demo: | |
gr.Markdown("# Plataforma de Generaci贸n de Informes de ViLAB", elem_id="title") | |
gr.Markdown("Importante: Plataforma de prueba experimental, se limita solo al uso did谩ctico.") | |
gr.Markdown("### Ingrese el nombre de su empresa y su logo [opcional].", elem_id="subtitle") | |
header_items = [] | |
with gr.Row(equal_height=True): | |
with gr.Column(scale=1, min_width=160): | |
nombre_empresa = gr.Textbox("Nombre de la Empresa", label=None, visible = True, container = False, elem_id="text-empresa", interactive=True) | |
header_items.append(nombre_empresa) | |
with gr.Column(scale=1, min_width=160): | |
logo = gr.Image(label="Logo", elem_id="logo-upload", visible=True, interactive=True, mirror_webcam=False, sources="upload") | |
header_items.append(logo) | |
gr.Markdown("---") | |
# ----------------------------------------------------------------------------------------------------- | |
gr.Markdown("## Plantilla de Informes") | |
gr.Markdown("### Ingrese los datos para el informe.") | |
gr.Markdown("### (Agregue campos con el bot贸n '+', remueva campos con el bot贸n '-')") | |
texts_state = gr.State(2) | |
textboxes = [] | |
for i in range(max_texts_rows): | |
with gr.Row(): | |
with gr.Column(scale=1, min_width=160): | |
textbox_l = gr.Textbox("T铆tulo: Descripci贸n", show_label=False, elem_classes="text-box", visible = (i<init_texts_rows_state), interactive=True ) | |
textboxes.append(textbox_l) | |
with gr.Column(scale=1, min_width=160): | |
if i == 0: | |
textbox_r = gr.Textbox("Fecha: " + datetime.now().strftime("%d/%m/%Y"), show_label=False, elem_classes="text-box", visible = (i<init_texts_rows_state), interactive=False ) | |
else: | |
textbox_r = gr.Textbox("T铆tulo: Descripci贸n", show_label=False, elem_classes="text-box", visible = (i<init_texts_rows_state), interactive=True ) | |
textboxes.append(textbox_r) | |
with gr.Row(): | |
with gr.Column(scale=1, min_width=160): | |
plus_button = gr.Button("+") | |
with gr.Column(scale=1, min_width=160): | |
minus_button = gr.Button("-") | |
def variable_outputs_plus(k): | |
k = int(k) | |
k = min(max_texts_elements, k + 1) | |
return [gr.Textbox(visible=True)]*(k) + [gr.Textbox(visible=False)]*(max_texts_elements-k) + [k] | |
def variable_outputs_minus(k): | |
k = int(k) | |
k = max(1, k - 1) | |
return [gr.Textbox(visible=True)]*(k) + [gr.Textbox(visible=False)]*(max_texts_elements-k) + [k] | |
plus_button.click(variable_outputs_plus, texts_state, textboxes + [texts_state]) | |
minus_button.click(variable_outputs_minus, texts_state, textboxes + [texts_state]) | |
gr.Markdown("---") | |
# ----------------------------------------------------------------------------------------------------- | |
gr.Markdown("## Plantilla de Resultados") | |
gr.Markdown("### Ingrese los resultados con imagen y descripci贸n.") | |
gr.Markdown("### (Agregue campos con el bot贸n '+', remueva campos con el bot贸n '-')") | |
images_description_state = gr.State(0) | |
img_desc_boxes = [] | |
for i in range(max_img_desc_rows): | |
with gr.Row(): | |
with gr.Column(scale=1, min_width=160): | |
image = gr.Image(label=f"Imagen {i}", interactive=True, mirror_webcam=False, sources="upload", elem_classes="image-upload", container = True, visible = (i<init_img_desc_rows_state)) | |
img_desc_boxes.append(image) | |
with gr.Column(scale=1, min_width=160): | |
description = gr.TextArea("Describa su muestra ... ", label=f"Descripci贸n", visible=(i<init_img_desc_rows_state), elem_classes="description", interactive=True, max_lines=10 ) | |
img_desc_boxes.append(description) | |
with gr.Row(): | |
with gr.Column(scale=1, min_width=160): | |
plus_img_desc_button = gr.Button("+") | |
with gr.Column(scale=1, min_width=160): | |
minus_img_desc_button = gr.Button("-") | |
def variable_outputs_plus_img_desc(k): | |
k = int(k) | |
k = min(max_img_desc_rows, k + 1) | |
return [gr.Image(visible=True), gr.TextArea(visible=True)]*k + [gr.Image(visible=False), gr.TextArea(visible=False)]*(max_img_desc_rows-k) + [k] | |
def variable_outputs_minus_img_desc(k): | |
k = int(k) | |
k = max(0, k - 1) | |
return [gr.TextArea(visible=True)]*(2*k) + [gr.TextArea(visible=False)]*(2*(max_texts_rows-k)) + [k] | |
plus_img_desc_button.click(variable_outputs_plus_img_desc, images_description_state, img_desc_boxes + [images_description_state]) | |
minus_img_desc_button.click(variable_outputs_minus_img_desc, images_description_state, img_desc_boxes + [images_description_state]) | |
gr.Markdown("---") | |
# ----------------------------------------------------------------------------------------------------- | |
state_data = gr.State([]) | |
with gr.Row(): | |
submit_button = gr.DownloadButton (label="Generar Reporte") | |
file_output = gr.File(interactive=False, visible=False) | |
# Funci贸n para mantener la relaci贸n de aspecto de la imagen | |
def get_aspect_ratio_image_size(img, max_width, max_height): | |
width, height = img.size | |
aspect_ratio = width / height | |
if width > max_width or height > max_height: | |
if width / max_width > height / max_height: | |
width = max_width | |
height = width / aspect_ratio | |
else: | |
height = max_height | |
width = height * aspect_ratio | |
return width, height | |
class PDF(FPDF): | |
def __init__(self, datos, *args, **kwargs): | |
super().__init__(*args, **kwargs) | |
self.datos = datos | |
def set_text(self, datos): | |
self.datos = datos | |
def footer(self): | |
self.set_y(-15) | |
self.set_font('Helvetica', '', 8) | |
self.cell(0, 10, 'P谩gina %s' % self.page_no(), 0, new_x='RIGHT', new_y='TOP', align='C') | |
def body(self): | |
# Datos de la Empresa | |
nombre_empresa = "Nombre de la Empresa: " + self.datos[0][0] | |
logo_path = self.datos[0][1] | |
if logo_path is not None: | |
image = Image.fromarray(logo_path) | |
logo_width, logo_height = get_aspect_ratio_image_size(image, 30, 30) | |
self.image(image, 10, self.get_y(), logo_width, logo_height) | |
self.ln(logo_height + 5) | |
self.set_font('Helvetica', '', 12) | |
self.cell(0, 10, nombre_empresa, 0, new_x='LMARGIN', new_y='NEXT', align='L') | |
# L铆nea horizontal divisoria | |
self.line(10, self.get_y(), self.w - 10, self.get_y()) | |
self.ln(5) | |
# Datos del Informe | |
self.set_font('Helvetica', '', 11) | |
for i in range(len(self.datos[1])): | |
if i%2 != 0: | |
if self.datos[1][i] != "T铆tulo: Descripci贸n": | |
self.cell(self.w / 2 - 10, 10, self.datos[1][i], 0, new_x='LMARGIN', new_y='NEXT', align='L') | |
if i%2 == 0: | |
if self.datos[1][i] != "T铆tulo: Descripci贸n": | |
self.cell(self.w / 2 - 10, 10, self.datos[1][i], 0, new_x='RIGHT', new_y='TOP', align='L') | |
# L铆nea horizontal divisoria | |
self.ln(10) | |
self.line(10, self.get_y(), self.w - 10, self.get_y()) | |
self.ln(5) | |
# Datos del Resultado | |
for i in range(len(self.datos[2])): | |
if self.datos[2][i] is not None: | |
image = Image.fromarray(self.datos[2][i]) | |
img_width, img_height = get_aspect_ratio_image_size(image, self.w / 2 - 20, 100) | |
if self.get_y() + img_height + 10 > self.h - 30: | |
self.add_page() | |
self.image(image, 10, self.get_y(), img_width, img_height) | |
if self.datos[3][i] != "Describa su muestra ... ": | |
self.set_font('Helvetica', '', 10) | |
self.set_xy(self.w / 2 + 10, self.get_y()) | |
self.multi_cell(self.w / 2 - 20, None, self.datos[3][i]) | |
self.ln(img_height + 5) | |
else: | |
if self.datos[3][i] != "Describa su muestra ... ": | |
self.set_font('Helvetica', '', 10) | |
if self.get_y() + 10 > self.h - 30: | |
self.add_page() | |
self.set_xy(self.w / 2 + 10, self.get_y()) | |
self.multi_cell(self.w / 2 - 20, None, self.datos[3][i]) | |
# Resumen del Informe | |
self.set_font('Helvetica', '', 10) | |
self.ln(10) # A帽adir espacio antes del resumen | |
self.multi_cell(0, 6, "Resumen - Historial Cl铆nico: " + self.datos[4], 0, 'L') | |
def get_values(*args): | |
n_args = len(args) | |
data = [ | |
list(args[0:2]), | |
list(args[2:2+max_texts_elements-1]), | |
list(args[2+max_texts_elements:n_args:2]), | |
list(args[2+max_texts_elements+1:n_args+1:2]) | |
] | |
nombre_empresa = data[0][0] | |
datos_informe = [i for i in data[1] if i != "T铆tulo: Descripci贸n"] | |
resultados_informe = [i for i in data[3] if i != "Describa su muestra ... "] | |
prompt = f"""Eres un narrador del historial cl铆nico en espa帽ol basado en el siguiente contexto: | |
------------------------------- | |
Nombre de la empresa: {nombre_empresa} | |
{' - '.join(datos_informe)} | |
Descripciones seg煤n las im谩genes digitalizadas con ViLAB desde el microscopio: {' - '.join(resultados_informe)} | |
------------------------------- | |
- Nunca generes informaci贸n extra fuera del contexto. | |
- A帽ade informaciones 煤tiles | |
Solo presenta el resumen del historial cl铆nico en un p谩rrafo. | |
Resumen:""" | |
chat_completion = client.chat.completions.create( | |
messages=[ | |
{ | |
"role": "user", | |
"content": prompt, | |
} | |
], | |
model="llama3-70b-8192", | |
) | |
data.append(chat_completion.choices[0].message.content) | |
# Crear PDF | |
pdf = PDF(data) | |
pdf.add_page() | |
pdf.body() | |
pdf_path = "./documento_generado.pdf" | |
pdf.output(pdf_path) | |
return gr.File(pdf_path, interactive=False, visible=True) | |
submit_button.click(get_values, header_items + textboxes + img_desc_boxes, file_output) | |
if __name__ == "__main__": | |
demo.launch(share=False) |