Download exportar.py from Yofran23/Venezuela: direct link, hf CLI and curl.
- Browser
- Download file 5.6 kB
-
https://huggingface.co/spaces/Yofran23/Venezuela/resolve/main/exportar.py
- Command line
-
hf download hf://spaces/Yofran23/Venezuela/exportar.py
-
curl -L -o exportar.py https://huggingface.co/spaces/Yofran23/Venezuela/resolve/main/exportar.py
5.6 kB
| """Utilidades de exportación a Excel y PDF.""" | |
| import io | |
| import os | |
| from datetime import datetime | |
| import pandas as pd | |
| try: | |
| from fpdf import FPDF | |
| PDF_AVAILABLE = True | |
| except ImportError: | |
| PDF_AVAILABLE = False | |
| try: | |
| import openpyxl # noqa: F401 — solo para verificar que está disponible | |
| EXCEL_AVAILABLE = True | |
| except ImportError: | |
| EXCEL_AVAILABLE = False | |
| def _pdf_safe(s) -> str: | |
| """La fuente core del PDF (Helvetica) solo soporta latin-1. | |
| Quitamos emojis y reemplazamos caracteres no soportados (guiones largos, etc.).""" | |
| if s is None: | |
| return "" | |
| s = (str(s) | |
| .replace("–", "-").replace("—", "-").replace("•", "-") | |
| .replace("✅", "[OK]").replace("👤", "(C)").replace("🆘", "").replace("…", "...")) | |
| return s.encode("latin-1", "ignore").decode("latin-1").strip() | |
| def _df_para_export(rows: list[dict]) -> pd.DataFrame: | |
| if not rows: | |
| return pd.DataFrame() | |
| df = pd.DataFrame(rows) | |
| cols_rename = { | |
| "nombre": "Nombre", | |
| "cedula": "Cédula", | |
| "edad": "Edad", | |
| "hospital": "Hospital", | |
| "condicion": "Condición", | |
| "descripcion": "Descripción (IA)", | |
| "fecha_ingreso": "Fecha de ingreso", | |
| "fecha_update": "Última actualización", | |
| "verificado": "Verificado", | |
| "contacto": "Contacto familiar", | |
| "notas": "Notas", | |
| "fuente": "Fuente del reporte", | |
| } | |
| df = df.rename(columns={k: v for k, v in cols_rename.items() if k in df.columns}) | |
| df["Verificado"] = df["Verificado"].apply(lambda x: "Sí" if x else "No") | |
| cols_orden = ["Nombre", "Cédula", "Edad", "Hospital", "Condición", | |
| "Última actualización", "Verificado", "Contacto familiar", | |
| "Notas", "Descripción (IA)", "Fuente del reporte", "Fecha de ingreso"] | |
| cols_presentes = [c for c in cols_orden if c in df.columns] | |
| return df[cols_presentes] | |
| def exportar_excel(rows: list[dict]) -> str | None: | |
| if not rows: | |
| return None | |
| df = _df_para_export(rows) | |
| ts = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| path = f"/tmp/busqueda_familiar_{ts}.xlsx" | |
| with pd.ExcelWriter(path, engine="openpyxl") as writer: | |
| df.to_excel(writer, index=False, sheet_name="Personas") | |
| ws = writer.sheets["Personas"] | |
| # ajustar anchos | |
| anchos = {"Nombre": 28, "Cédula": 14, "Edad": 6, "Hospital": 32, | |
| "Condición": 16, "Última actualización": 20, "Verificado": 10, | |
| "Contacto familiar": 18, "Notas": 30, "Descripción (IA)": 40, | |
| "Fuente del reporte": 18, "Fecha de ingreso": 20} | |
| for i, col in enumerate(df.columns, 1): | |
| ws.column_dimensions[ws.cell(1, i).column_letter].width = anchos.get(col, 15) | |
| return path | |
| class _PDF(FPDF): | |
| def __init__(self, titulo: str): | |
| super().__init__(orientation="L", unit="mm", format="A4") | |
| self._titulo = titulo | |
| def header(self): | |
| self.set_font("Helvetica", "B", 13) | |
| self.set_fill_color(185, 28, 28) | |
| self.set_text_color(255, 255, 255) | |
| self.cell(0, 10, _pdf_safe(self._titulo), align="C", fill=True, new_x="LMARGIN", new_y="NEXT") | |
| self.set_text_color(0, 0, 0) | |
| self.set_font("Helvetica", size=8) | |
| self.cell(0, 6, _pdf_safe(f"Generado: {datetime.now().strftime('%d/%m/%Y %H:%M')}"), | |
| align="R", new_x="LMARGIN", new_y="NEXT") | |
| self.ln(2) | |
| def footer(self): | |
| self.set_y(-12) | |
| self.set_font("Helvetica", "I", 8) | |
| self.set_text_color(120, 120, 120) | |
| self.cell(0, 6, _pdf_safe(f"Plataforma Buscador Familiar - Terremoto Venezuela | Pag. {self.page_no()}"), | |
| align="C") | |
| def exportar_pdf(rows: list[dict]) -> str | None: | |
| if not rows or not PDF_AVAILABLE: | |
| return None | |
| df = _df_para_export(rows) | |
| # columnas que caben bien en landscape A4 | |
| cols_pdf = ["Nombre", "Cédula", "Edad", "Hospital", "Condición", | |
| "Última actualización", "Verificado"] | |
| cols_pdf = [c for c in cols_pdf if c in df.columns] | |
| anchos_pdf = { | |
| "Nombre": 55, "Cédula": 26, "Edad": 12, "Hospital": 60, | |
| "Condición": 28, "Última actualización": 36, "Verificado": 18, | |
| } | |
| pdf = _PDF("Busqueda de Familiares - Terremoto Venezuela") | |
| pdf.add_page() | |
| # cabecera de tabla | |
| pdf.set_font("Helvetica", "B", 9) | |
| pdf.set_fill_color(220, 38, 38) | |
| pdf.set_text_color(255, 255, 255) | |
| for col in cols_pdf: | |
| pdf.cell(anchos_pdf.get(col, 30), 8, _pdf_safe(col), border=1, fill=True, align="C") | |
| pdf.ln() | |
| # filas | |
| pdf.set_font("Helvetica", size=8) | |
| pdf.set_text_color(0, 0, 0) | |
| fill = False | |
| for _, row in df.iterrows(): | |
| pdf.set_fill_color(255, 235, 235) if fill else pdf.set_fill_color(255, 255, 255) | |
| for col in cols_pdf: | |
| val = _pdf_safe(row.get(col, "")) | |
| if len(val) > 35: | |
| val = val[:33] + "..." | |
| pdf.cell(anchos_pdf.get(col, 30), 7, val, border=1, fill=True) | |
| pdf.ln() | |
| fill = not fill | |
| # nota al pie del contenido | |
| pdf.ln(4) | |
| pdf.set_font("Helvetica", "I", 7) | |
| pdf.set_text_color(100, 100, 100) | |
| pdf.multi_cell(0, 5, _pdf_safe( | |
| "AVISO: Esta informacion es de caracter orientativo. Verifique siempre con el personal " | |
| "del hospital. [OK] = Verificado por personal medico - No = Reportado por ciudadano.")) | |
| ts = datetime.now().strftime("%Y%m%d_%H%M%S") | |
| path = f"/tmp/busqueda_familiar_{ts}.pdf" | |
| pdf.output(path) | |
| return path | |