Csvzip / app.py
Minacrypto's picture
Update app.py
94a5577 verified
Raw
History Blame Contribute Delete
9.58 kB
import gradio as gr
import pandas as pd
import requests
import base64
import os
from weasyprint import HTML
def obtener_url_hd(url):
"""
Transforma la URL para saltarse la compresión de 400x0 píxeles
y obtener el archivo maestro original directo de Amazon S3.
"""
if "filters:format(webp)/" in str(url):
raw_s3 = str(url).split("filters:format(webp)/")[1]
if not raw_s3.startswith("http"):
raw_s3 = "https://" + raw_s3
return raw_s3
# Alternativa por si el formato cambia: pedir tamaño grande de 1200px
return str(url).replace("400x0", "1200x0")
def generar_catalogo(file_obj):
if file_obj is None:
return "Por favor, sube un archivo CSV válido.", None
try:
# 1. Leer el archivo CSV cargado por el usuario
df = pd.read_csv(file_obj.name)
# Verificar que tenga las columnas necesarias
columnas_requeridas = ['name', 'price', 'image_url']
for col in columnas_requeridas:
if col not in df.columns:
return f"Falta la columna obligatoria '{col}' en tu archivo CSV.", None
# 2. Construir la cabecera del HTML con diseño elegante estilo joyería
html_content = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
@page {
size: A4;
margin: 20mm 15mm;
background-color: #ffffff;
@bottom-right {
content: "Página " counter(page);
font-family: 'Times New Roman', serif;
font-size: 9pt;
color: #888;
}
}
*, *::before, *::after { box-sizing: border-box; }
body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
margin: 0;
padding: 0;
color: #2c3e50;
background-color: #ffffff;
}
.cover {
text-align: center;
padding: 30px 0;
border-bottom: 2px solid #d4af37;
margin-bottom: 30px;
}
.cover h1 {
font-family: 'Times New Roman', Times, serif;
font-size: 26pt;
letter-spacing: 4px;
margin: 0 0 10px 0;
color: #1a1a1a;
text-transform: uppercase;
}
.cover p {
font-size: 11pt;
font-style: italic;
color: #7f8c8d;
margin: 0;
letter-spacing: 1px;
}
.product-card {
display: block;
margin-bottom: 25px;
padding-bottom: 25px;
border-bottom: 1px dashed #e0e0e0;
page-break-inside: avoid;
}
.product-table {
display: table;
width: 100%;
}
.product-row {
display: table-row;
}
.product-img-cell {
display: table-cell;
width: 150px;
vertical-align: top;
padding-right: 25px;
}
.product-info-cell {
display: table-cell;
vertical-align: top;
}
.img-real {
width: 150px;
height: 150px;
object-fit: cover;
border: 1px solid #e2e8f0;
border-radius: 4px;
}
.product-title {
font-family: 'Times New Roman', Times, serif;
font-size: 14pt;
font-weight: bold;
color: #1a1a1a;
margin: 0 0 8px 0;
line-height: 1.3;
}
.product-price {
font-size: 13pt;
font-weight: bold;
color: #d4af37;
margin-bottom: 15px;
}
.btn-comprar {
display: inline-block;
padding: 8px 18px;
background-color: #1a1a1a;
color: #ffffff !important;
text-align: center;
text-decoration: none !important;
font-size: 10pt;
border-radius: 4px;
font-weight: bold;
text-transform: uppercase;
letter-spacing: 1px;
margin-top: 5px;
border: 1px solid #1a1a1a;
}
.badge {
display: inline-block;
font-size: 8pt;
background-color: #f4ece1;
color: #8a6d3b;
padding: 2px 8px;
border-radius: 3px;
margin-bottom: 8px;
text-transform: uppercase;
font-weight: bold;
}
</style>
</head>
<body>
<div class="cover">
<h1>Catálogo de Joyería Exclusiva</h1>
<p>Generado Automáticamente en Alta Definición (Ultra-HD)</p>
</div>
<div class="product-grid">
"""
# 3. Descargar imágenes limpiando la URL para forzar ALTA DEFINICIÓN
for _, row in df.iterrows():
name = str(row['name'])
price = str(row['price'])
url_original = str(row['image_url'])
# ¡AQUÍ SE PRODUCE LA MAGIA HD! Transformamos la URL comprimida a la URL maestra
url_hd = obtener_url_hd(url_original)
clean_name = name.replace("Unisex6mm", "").replace("Unisex4mm", "").replace("Male10mm", "").replace("Male6MM", "").replace("Male11mm", "").replace("Male8MM", "").strip()
gender_badge = "Unisex" if "Unisex" in name else "Hombre" if "Male" in name else "Exclusivo"
img_src = url_hd # Fallback por si acaso
try:
headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)'}
r = requests.get(url_hd, headers=headers, timeout=20)
if r.status_code == 200:
encoded_img = base64.b64encode(r.content).decode('utf-8')
# Identificar formato real del archivo HD
mime_type = "image/jpeg"
if "png" in url_hd.lower():
mime_type = "image/png"
elif "webp" in url_hd.lower():
mime_type = "image/webp"
img_src = f"data:{mime_type};base64,{encoded_img}"
except Exception as e:
print(f"No se pudo incrustar directamente la imagen HD {url_hd}: {e}")
img_src = url_hd
# Agregar tarjeta de producto al catálogo HTML con el botón de compra corregido
html_content += f"""
<div class="product-card">
<div class="product-table">
<div class="product-row">
<div class="product-img-cell">
<img class="img-real" src="{img_src}">
</div>
<div class="product-info-cell">
<div class="badge">{gender_badge}</div>
<div class="product-title">{clean_name}</div>
<div class="product-price">{price}</div>
<div>
<!-- SE ELIMINÓ target="_blank" PARA EVITAR EL MENSAJE DE ADVERTENCIA EN EL PDF -->
<a class="btn-comprar" href="https://tally.so/r/5BQpMv">COMPRAR</a>
</div>
</div>
</div>
</div>
</div>
"""
html_content += """
</div>
</body>
</html>
"""
# 4. Compilar el HTML a PDF usando WeasyPrint con las imágenes en alta fidelidad
output_pdf = "catalogo_ultra_hd.pdf"
HTML(string=html_content).write_pdf(output_pdf)
return "¡Catálogo procesado con éxito! Se ha generado el catálogo con los botones de compra directo.", output_pdf
except Exception as e:
return f"Ocurrió un error inesperado al procesar el archivo: {str(e)}", None
# 5. Diseñar la interfaz gráfica en Gradio
with gr.Blocks(theme=gr.themes.Soft()) as demo:
gr.Markdown("# 💎 Generador de Catálogos Joyeros en Ultra-HD (Versión Enlaces Directos)")
gr.Markdown("Esta versión genera el catálogo eliminando el atributo que provocaba el mensaje de advertencia del lector de PDF.")
with gr.Row():
with gr.Column():
archivo_input = gr.File(label="Sube tu archivo CSV (products.csv)", file_types=[".csv"])
btn_procesar = gr.Button("✨ Generar PDF con Botón de Compra Directo", variant="primary")
with gr.Column():
estado_output = gr.Textbox(label="Estado del Proceso", placeholder="Esperando archivo...")
archivo_output = gr.File(label="Descarga tu PDF Aquí")
btn_procesar.click(
fn=generar_catalogo,
inputs=archivo_input,
outputs=[estado_output, archivo_output]
)
if __name__ == "__main__":
demo.launch()