Generazione di Schede Tecniche: Creazione di PDF su Richiesta e in Batch

Questo articolo spiega come generiamo le schede tecniche dei prodotti in formato PDF, sia su richiesta (quando gli utenti le richiedono) che in batch (pre-generandole per i prodotti più popolari).

Il Problema: Creare Schede Tecniche dei Prodotti

Ogni prodotto necessita di una scheda tecnica PDF professionale con:

  • Nome e immagini del prodotto

  • Specifiche tecniche raggruppate per categoria

  • Brand aziendale e informazioni di contatto

  • Layout ottimizzato (pagina singola, colonne bilanciate)

Generare PDF ad ogni richiesta è lento (~2 secondi per PDF). Pre-generare tutti i 65.000 SKU spreca spazio di archiviazione e tempo.

La Soluzione: Approccio Ibrido

Utilizziamo due metodi di generazione:

Su richiesta: Genera il PDF quando l'utente lo richiede (la prima volta)

Batch: Pre-genera i PDF per i prodotti più popolari (ogni notte)

Generazione su Richiesta

Struttura URL

/ds/<sku>.pdf
/ds/<sku>

Entrambi gli URL generano lo stesso PDF.

Flusso della Richiesta

@web.route("/ds/<sku>.pdf")
def datasheet(sku: str):
    # Valida SKU
# ... (dettagli implementativi omessi)

Processo di Generazione

Passo 1: Ottieni Dati del Prodotto

# Nome prodotto
name = expand_sku(sku)

# ... (dettagli implementativi omessi)

Passo 2: Bilanciamento Colonne

Le caratteristiche sono distribuite su 3 colonne per bilanciare il numero di righe:

# Calcola righe per gruppo di caratteristiche
all_groups = []
for heading, f_items in features.items():
# ... (dettagli implementativi omessi)

Passo 3: Calcola Altezze Immagini

# Altezza immagine principale (proporzionale alle proporzioni)
with Image.open(main_image_path) as img:
    main_width, main_height = img.size
# ... (dettagli implementativi omessi)

Passo 4: Calcola Spaziatore

Spinge il piè di pagina in fondo alla pagina:

available_height = 25.0  # cm
content_height = total_image_height + text_height_cm
spacer_height = max(0, available_height - content_height)

Passo 5: Render HTML

html = render_template(
    "datasheet.html",
    sku=sku,
# ... (dettagli implementativi omessi)

Passo 6: Genera PDF

options = {
    "page-size": "A4",
    "enable-local-file-access": None,
    "load-error-handling": "ignore",
    "load-media-error-handling": "ignore",
    "no-stop-slow-scripts": None
}

pdf_data = pdfkit.from_string(html, options=options)

Passo 7: Mantieni Solo la Prima Pagina

reader = PdfReader(BytesIO(pdf_data))
if len(reader.pages) > 1:
    writer = PdfWriter()
    writer.add_page(reader.pages[0])
    output = BytesIO()
    writer.write(output)
    pdf_data = output.getvalue()

Prestazioni

Tempo di generazione: ~2 secondi per PDF

Caching: Nessun caching (sempre aggiornato)

Vantaggio: Sempre aggiornato con i dati più recenti del prodotto

Generazione in Batch

Scopo

Pre-generare PDF per i prodotti più popolari per ridurre il carico su richiesta.

Posizione Script

scripts/utils/generate_datasheets.py

Tracciamento Cambiamenti

Tracciamo le modifiche alle sezioni di productdb.json rilevanti per ogni SKU:

def get_sku_productdb_hash(sku: str) -> str:
    """Ottieni l'hash delle sezioni di productdb rilevanti per questo SKU."""
    with open(PRODUCTDB_PATH, "r") as f:
# ... (dettagli implementativi omessi)

Priorità

Diamo priorità agli SKU in base alla frequenza di richiesta:

def load_popular_skus() -> List[str]:
    """Carica SKU popolari dai log di accesso."""
    try:
# ... (dettagli implementativi omessi)

Strategia di Generazione

def batch_generate():
    # Carica cache hash
    hash_cache = load_hash_cache()
# ... (dettagli implementativi omessi)

Programmazione

La generazione batch viene eseguita ogni notte tramite cron:

0 2 * * * cd /home/ubuntu/manage && python scripts/utils/generate_datasheets.py

Archiviazione

Locale: /home/ubuntu/web-static/ds/<sku>.pdf

S3: s3://thinvent-web-static/ds/<sku>.pdf

CDN: Servito via CloudFront

Struttura del Template

Il template della scheda tecnica utilizza un layout a 3 colonne:

<div class="container">
  <!-- Intestazione con logo e nome prodotto -->
  <div class="header">
    <img src="logo.png">
    <h1>{{ name }}</h1>
  </div>

  <!-- Immagine principale -->
  <img src="{{ images[0][1] }}" style="width: 9.5cm">

  <!-- Miniature (fino a 4) -->
  <div class="thumbnails">
    {% for name, url in images[1:5] %}
      <img src="{{ url }}" style="width: 4.5cm">
    {% endfor %}
  </div>

  <!-- Caratteristiche in 3 colonne -->
  <div class="features">
    <div class="column">
      {% for heading, items in features1 %}
        <h3>{{ heading }}</h3>
        {% for name, value in items.items() %}
          <div><strong>{{ name }}:</strong> {{ value }}</div>
        {% endfor %}
      {% endfor %}
    </div>

    <div class="column">
      <!-- features2 -->
    </div>

    <div class="column">
      <!-- features3 -->
    </div>
  </div>

  <!-- Spaziatore per spingere il piè di pagina in basso -->
  <div style="height: {{ spacer_height }}cm"></div>

  <!-- Piè di pagina con informazioni di contatto -->
  <div class="footer">
    <p>www.thinvent.in | sales@thinvent.in | +91-124-4343177</p>
  </div>
</div>

Configurazione

Le costanti controllano il layout:

DATASHEET_COLUMN_COUNT = 3
DATASHEET_HEADING_LINE_WEIGHT = 2  # Righe per intestazione
DATASHEET_FEATURE_NAME_MAX_LEN = 30  # Caratteri prima del wrapping
DATASHEET_FEATURE_VALUE_CHARS_PER_LINE = 40  # Caratteri per riga
PDF_PAGE_SIZE = "A4"

Punti di Integrazione

Pagine Prodotto

Link alla scheda tecnica:

<a href="/ds/{{ sku }}.pdf" target="_blank">Scarica Scheda Tecnica</a>

Google Shopping

Schede tecniche collegate nel feed prodotto:

<g:product_detail>
  <g:section_name>Scheda Tecnica</g:section_name>
  <g:attribute_name>PDF</g:attribute_name>
  <g:attribute_value>https://www.thinvent.in/ds/{{ sku }}.pdf</g:attribute_value>
</g:product_detail>

Campagne Email

Schede tecniche allegate alle email di preventivo.

Gestione Errori

SKU Non Valido

if not check_sku(sku):
    abort(422, description="Prodotto non trovato.")

Fallimento Generazione

try:
    pdf_data = generate_datasheet_pdf(sku)
    if pdf_data is None:
        abort(500, description="Generazione PDF fallita.")
except Exception as e:
    logger.error(f"Generazione PDF fallita per {sku}: {e}")
    abort(500, description="Generazione PDF fallita.")

Immagini Mancanti

try:
    with Image.open(image_path) as img:
        width, height = img.size
except Exception:
    # Usa dimensioni predefinite
    width, height = 800, 600

Riferimenti

Librerie

  • pdfkit - Conversione da HTML a PDF

  • pypdf - Manipolazione PDF

  • Pillow - Elaborazione immagini

Articoli Correlati

Riepilogo

La generazione delle schede tecniche utilizza un approccio ibrido:

Su Richiesta:

  • ✅ Genera quando l'utente richiede

  • ✅ Sempre aggiornato

  • ✅ Nessuno spreco di archiviazione

  • ✅ ~2 secondi per PDF

Batch:

  • ✅ Pre-genera prodotti popolari

  • ✅ Traccia cambiamenti productdb

  • ✅ Priorità per frequenza di richiesta

  • ✅ Lavoro cron notturno

Processo:

  • ✅ Ottieni dati prodotto (nome, immagini, caratteristiche, descrizione)

  • ✅ Bilanciamento caratteristiche su 3 colonne

  • ✅ Calcola altezze immagini

  • ✅ Render template HTML

  • ✅ Genera PDF con pdfkit

  • ✅ Mantieni solo la prima pagina

Archiviazione:

  • ✅ Locale: /home/ubuntu/web-static/ds/

  • ✅ S3: s3://thinvent-web-static/ds/

  • ✅ CDN: Distribuzione CloudFront

Questo approccio ibrido bilancia l'aggiornamento (su richiesta) con le prestazioni (pre-generazione batch).


← Torna all'Indice della Documentazione