Spaces:
Sleeping
Sleeping
File size: 12,848 Bytes
11ff1d2 c0be5e3 e11c4cc a798166 11ff1d2 10fa36b 388c630 a798166 e11c4cc 10fa36b e11c4cc 388c630 10fa36b 388c630 10fa36b 388c630 10fa36b e11c4cc 10fa36b e11c4cc 10fa36b e11c4cc 10fa36b e11c4cc 10fa36b e11c4cc 388c630 e11c4cc 10fa36b 388c630 10fa36b e11c4cc 10fa36b e11c4cc 10fa36b e11c4cc 10fa36b 63995e0 a798166 e11c4cc 10fa36b a798166 10fa36b a798166 e11c4cc a798166 e11c4cc a798166 10fa36b e11c4cc a798166 e11c4cc 10fa36b e11c4cc 388c630 e11c4cc a798166 98e4ea6 e11c4cc c0be5e3 98e4ea6 e11c4cc 26d5568 |
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 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
import gradio as gr
import os
import tempfile
from pdf2image import convert_from_path
import logging
from commercial_invoice import process_invoice_batch, logger
from hawb_processing import process_hawb_batch
# Configurar handler para capturar logs
class GradioHandler(logging.Handler):
def __init__(self, log_history):
super().__init__()
self.log_history = log_history
def emit(self, record):
log_entry = self.format(record)
self.log_history.append(log_entry)
def procesar_pdf_invoice(pdf_path, progress=gr.Progress()):
"""Procesa un archivo PDF y extrae información de las facturas"""
log_history = []
handler = GradioHandler(log_history)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
try:
# Validar que el nombre del archivo comience con "ci145"
filename = os.path.basename(pdf_path).lower()
if not filename.startswith("ci145"):
error_msg = """
<div style='color: red; font-weight: bold; padding: 10px; border: 1px solid red; border-radius: 5px; background-color: #ffe6e6;'>
⚠️ El tipo de archivo cargado no es compatible con el formato de extracción que maneja este programa, el cual está customizado para manejar únicamente los archivos que empiezan por "ci145".
</div>
"""
logger.error("Archivo incompatible: " + filename)
return error_msg, None, "\n".join(log_history)
progress(0.1, desc="Iniciando procesamiento...")
# Crear directorio temporal para las imágenes
with tempfile.TemporaryDirectory() as temp_dir:
logger.info(f"Convirtiendo PDF a imágenes: {pdf_path}")
progress(0.2, desc="Convirtiendo PDF a imágenes...")
# Convertir PDF a imágenes
images = convert_from_path(pdf_path)
image_paths = []
# Guardar imágenes temporalmente
for i, image in enumerate(images):
progress((0.3 + (i/len(images) * 0.2)), desc=f"Procesando página {i+1} de {len(images)}...")
image_path = os.path.join(temp_dir, f'pagina_{i+1}.jpg')
image.save(image_path, 'JPEG')
image_paths.append(image_path)
logger.info(f"Se generaron {len(image_paths)} imágenes")
progress(0.5, desc="Procesando imágenes...")
# Procesar las imágenes
coordinates_json = "./coordinates_CI.json"
results_df = process_invoice_batch(image_paths, coordinates_json)
progress(0.8, desc="Guardando resultados...")
# Guardar resultados
output_dir = "./data"
os.makedirs(output_dir, exist_ok=True)
# Guardar archivo de facturas procesadas
facturas_path = os.path.join(output_dir, 'facturas_procesadas.csv')
results_df.to_csv(facturas_path, index=False)
progress(0.9, desc="Finalizando...")
# Preparar mensaje de resultados con estilo
mensaje = """
<div style='padding: 10px; border: 1px solid #4CAF50; border-radius: 5px; background-color: #f1f8e9;'>
<h3 style='color: #2E7D32; margin-top: 0;'>✅ Procesamiento completado</h3>
<ul style='list-style-type: none; padding-left: 0;'>
<li>📄 Páginas procesadas: {}</li>
<li>📊 Campos extraídos: {}</li>
</ul>
<h4 style='color: #2E7D32;'>Archivos generados:</h4>
<ol>
<li>facturas_procesadas.csv</li>
""".format(len(image_paths), len(results_df.columns)-1)
# Verificar archivo de productos
productos_path = os.path.join(output_dir, 'productos_por_factura.csv')
if os.path.exists(productos_path):
mensaje += "<li>productos_por_factura.csv</li>"
mensaje += "</ol></div>"
progress(1.0, desc="¡Proceso completado!")
return mensaje, [facturas_path, productos_path], "\n".join(log_history)
except Exception as e:
error_msg = f"""
<div style='color: red; font-weight: bold; padding: 10px; border: 1px solid red; border-radius: 5px; background-color: #ffe6e6;'>
⚠️ Error durante el procesamiento: {str(e)}
</div>
"""
logger.error(error_msg)
return error_msg, None, "\n".join(log_history)
finally:
logger.removeHandler(handler)
def procesar_guia_aerea(pdf_path, progress=gr.Progress()):
"""Procesa un archivo PDF de guía aérea y extrae las imágenes"""
log_history = []
handler = GradioHandler(log_history)
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
try:
# Validar que el nombre del archivo comience con "hawb145"
filename = os.path.basename(pdf_path).lower()
if not filename.startswith("hawb145"):
error_msg = """
<div style='color: red; font-weight: bold; padding: 10px; border: 1px solid red; border-radius: 5px; background-color: #ffe6e6;'>
⚠️ El archivo debe comenzar con "hawb145" para ser procesado como guía aérea.
</div>
"""
logger.error("Archivo incompatible: " + filename)
return error_msg, None, "\n".join(log_history)
progress(0.1, desc="Iniciando procesamiento...")
# Usar directorio temporal para las imágenes
with tempfile.TemporaryDirectory() as temp_dir:
logger.info(f"Convirtiendo PDF a imágenes: {pdf_path}")
progress(0.2, desc="Convirtiendo PDF a imágenes...")
# Convertir PDF a imágenes
images = convert_from_path(pdf_path)
image_paths = []
# Guardar imágenes en el directorio temporal
for i, image in enumerate(images):
progress((0.3 + (i/len(images) * 0.6)), desc=f"Procesando página {i+1} de {len(images)}...")
image_path = os.path.join(temp_dir, f'hawb_pagina_{i+1}.jpg')
image.save(image_path, 'JPEG')
image_paths.append(image_path)
logger.info(f"Se generaron {len(image_paths)} imágenes en directorio temporal")
progress(0.5, desc="¡Proceso completado!")
# Procesar las imágenes
coordinates_json = "./coordinates_HAWB.json"
results_df = process_hawb_batch(image_paths, coordinates_json)
output_dir = "data"
os.makedirs(output_dir, exist_ok=True)
csv_path = os.path.join(output_dir, 'hawb_procesados.csv')
results_df.to_csv(csv_path, index=False)
logger.info(f"Resultados guardados en: {csv_path}")
progress(0.9, desc="Finalizando...")
# Preparar mensaje de resultados con estilo
mensaje = f"""
<div style='padding: 10px; border: 1px solid #4CAF50; border-radius: 5px; background-color: #f1f8e9;'>
<h3 style='color: #2E7D32; margin-top: 0;'>✅ Procesamiento completado</h3>
<ul style='list-style-type: none; padding-left: 0;'>
<li>📄 Páginas procesadas: {len(image_paths)}</li>
<li>📁 Imágenes procesadas en memoria temporal</li>
</ul>
</div>
"""
return mensaje, csv_path, "\n".join(log_history)
except Exception as e:
error_msg = f"""
<div style='color: red; font-weight: bold; padding: 10px; border: 1px solid red; border-radius: 5px; background-color: #ffe6e6;'>
⚠️ Error durante el procesamiento: {str(e)}
</div>
"""
logger.error(error_msg)
return error_msg, None, "\n".join(log_history)
finally:
logger.removeHandler(handler)
def crear_interfaz():
with gr.Blocks(css="""
.message { margin-bottom: 20px; }
.error { color: red; font-weight: bold; }
.logs {
font-family: monospace;
background-color: #f5f5f5;
padding: 10px;
border-radius: 5px;
max-height: 200px;
overflow-y: auto;
}
.tab-selected {
border-bottom: 2px solid #2196F3 !important;
}
""") as demo:
gr.Markdown("# Extractor de Información de Facturas y Guías Aéreas")
with gr.Tabs() as tabs:
with gr.TabItem("Facturas Comerciales", id=1):
with gr.Row():
pdf_input = gr.File(
label="Cargar PDF de facturas (el nombre debe empezar con 'ci145')",
file_types=[".pdf"]
)
with gr.Row():
procesar_facturas_btn = gr.Button(
"Procesar PDF de Facturas",
variant="primary",
elem_classes=["custom-button"]
)
with gr.Row():
output_text = gr.HTML(
label="Resultados"
)
with gr.Row():
log_output = gr.Textbox(
label="Logs del proceso",
elem_classes=["logs"],
lines=10,
max_lines=10,
show_label=True
)
with gr.Row():
files_output = gr.File(
label="Descargar archivos CSV",
file_count="multiple",
interactive=False,
visible=False
)
with gr.TabItem("Guías Aéreas", id=2):
with gr.Row():
guia_aerea_input = gr.File(
label="Cargar PDF de guía aérea. (El nombre debe empezar con 'hawb145')",
file_types=[".pdf"]
)
with gr.Row():
procesar_guia_btn = gr.Button(
"Procesar PDF de Guías Aéreas",
variant="primary",
elem_classes=["custom-button"]
)
with gr.Row():
guia_output_text = gr.HTML(
label="Resultados"
)
with gr.Row():
guia_log_output = gr.Textbox(
label="Logs del proceso",
elem_classes=["logs"],
lines=10,
max_lines=10,
show_label=True
)
with gr.Row():
guia_files_output = gr.File(
label="Descargar archivos procesados",
file_count="multiple",
interactive=False,
visible=False
)
def process_and_return_invoices(pdf_path):
message, csv_files, logs = procesar_pdf_invoice(pdf_path)
return message, csv_files if csv_files else None, logs
def process_and_return_guia(pdf_path):
message, files, logs = procesar_guia_aerea(pdf_path)
return message, files if files else None, logs
# Eventos de los botones
procesar_facturas_btn.click(
fn=process_and_return_invoices,
inputs=[pdf_input],
outputs=[output_text, files_output, log_output],
show_progress=True
).then(
lambda: gr.update(visible=True),
None,
[files_output]
)
procesar_guia_btn.click(
fn=process_and_return_guia,
inputs=[guia_aerea_input],
outputs=[guia_output_text, guia_files_output, guia_log_output],
show_progress=True
).then(
lambda: gr.update(visible=True),
None,
[guia_files_output]
)
return demo
if __name__ == "__main__":
demo = crear_interfaz()
demo.launch(share=True) |