Datenblatt-Generierung: On-Demand und Batch-PDF-Erstellung

Dieser Artikel erklärt, wie wir Produktdatenblätter als PDFs generieren, sowohl on-demand (wenn Benutzer sie anfordern) als auch im Batch (Vorgenerierung für beliebte Produkte).

Das Problem: Erstellung von Produktdatenblättern

Jedes Produkt benötigt ein professionelles PDF-Datenblatt mit:

  • Produktname und Bildern

  • Technischen Spezifikationen, nach Kategorien gruppiert

  • Firmenbranding und Kontaktinformationen

  • Optimiertem Layout (einzelne Seite, ausgeglichene Spalten)

PDFs bei jeder Anfrage zu generieren, ist langsam (~2 Sekunden pro PDF). Das Vorgenerieren aller 65.000 SKUs verschwendet Speicherplatz und Zeit.

Die Lösung: Hybrider Ansatz

Wir verwenden zwei Generierungsmethoden:

On-demand: PDF generieren, wenn der Benutzer es anfordert (erstmalig)

Batch: PDFs für beliebte Produkte vorgenerieren (täglich nachts)

On-Demand-Generierung

URL-Struktur

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

Beide URLs generieren dasselbe PDF.

Anfrageablauf

@web.route("/ds/<sku>.pdf")
def datasheet(sku: str):
    # Validate SKU
# ... (implementation details omitted)

Generierungsprozess

Schritt 1: Produktdaten abrufen

# Product name
name = expand_sku(sku)

# ... (implementation details omitted)

Schritt 2: Spalten ausbalancieren

Features werden auf 3 Spalten verteilt, um die Zeilenanzahl auszugleichen:

# Calculate lines per feature group
all_groups = []
for heading, f_items in features.items():
# ... (implementation details omitted)

Schritt 3: Bildhöhen berechnen

# Main image height (proportional to aspect ratio)
with Image.open(main_image_path) as img:
    main_width, main_height = img.size
# ... (implementation details omitted)

Schritt 4: Abstandshalter berechnen

Fußzeile an den Seitenboden schieben:

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

Schritt 5: HTML rendern

html = render_template(
    "datasheet.html",
    sku=sku,
# ... (implementation details omitted)

Schritt 6: PDF generieren

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)

Schritt 7: Nur erste Seite behalten

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()

Leistung

Generierungszeit: ~2 Sekunden pro PDF

Caching: Kein Caching (immer aktuell)

Vorteil: Immer auf dem neuesten Stand mit den aktuellsten Produktdaten

Batch-Generierung

Zweck

PDFs für beliebte Produkte vorgenerieren, um die On-Demand-Last zu reduzieren.

Skriptspeicherort

scripts/utils/generate_datasheets.py

Änderungen nachverfolgen

Wir verfolgen Änderungen an den für jede SKU relevanten Abschnitten der productdb.json:

def get_sku_productdb_hash(sku: str) -> str:
    """Get hash of productdb sections relevant to this SKU."""
    with open(PRODUCTDB_PATH, "r") as f:
# ... (implementation details omitted)

Priorisierung

Wir priorisieren SKUs nach Anfragehäufigkeit:

def load_popular_skus() -> List[str]:
    """Load popular SKUs from access logs."""
    try:
# ... (implementation details omitted)

Generierungsstrategie

def batch_generate():
    # Load hash cache
    hash_cache = load_hash_cache()
# ... (implementation details omitted)

Zeitplanung

Die Batch-Generierung läuft nachts via cron:

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

Speicherung

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

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

CDN: Ausgeliefert via CloudFront

Template-Struktur

Das Datenblatt-Template verwendet ein 3-Spalten-Layout:

<div class="container">
  <!-- Header with logo and product name -->
  <div class="header">
    <img src="logo.png">
    <h1>{{ name }}</h1>
  </div>

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

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

  <!-- Features in 3 columns -->
  <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>

  <!-- Spacer to push footer down -->
  <div style="height: {{ spacer_height }}cm"></div>

  <!-- Footer with contact info -->
  <div class="footer">
    <p>www.thinvent.in | sales@thinvent.in | +91-124-4343177</p>
  </div>
</div>

Konfiguration

Konstanten steuern das Layout:

DATASHEET_COLUMN_COUNT = 3
DATASHEET_HEADING_LINE_WEIGHT = 2  # Lines per heading
DATASHEET_FEATURE_NAME_MAX_LEN = 30  # Chars before wrapping
DATASHEET_FEATURE_VALUE_CHARS_PER_LINE = 40  # Chars per line
PDF_PAGE_SIZE = "A4"

Integrationspunkte

Produktseiten

Link zum Datenblatt:

<a href="/ds/{{ sku }}.pdf" target="_blank">Download Datasheet</a>

Google Shopping

Datenblätter im Produktfeed verlinkt:

<g:product_detail>
  <g:section_name>Datasheet</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>

E-Mail-Kampagnen

Datenblätter an Angebots-E-Mails angehängt.

Fehlerbehandlung

Ungültige SKU

if not check_sku(sku):
    abort(422, description="Product not found.")

Generierungsfehler

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

Fehlende Bilder

try:
    with Image.open(image_path) as img:
        width, height = img.size
except Exception:
    # Use default dimensions
    width, height = 800, 600

Referenzen

Bibliotheken

  • pdfkit - HTML-zu-PDF-Konvertierung

  • pypdf - PDF-Manipulation

  • Pillow - Bildverarbeitung

Verwandte Artikel

Zusammenfassung

Die Datenblatt-Generierung verwendet einen hybriden Ansatz:

On-Demand:

  • ✅ Generieren, wenn der Benutzer anfordert

  • ✅ Immer aktuell

  • ✅ Keine Speicherverschwendung

  • ✅ ~2 Sekunden pro PDF

Batch:

  • ✅ Beliebte Produkte vorgenerieren

  • ✅ productdb-Änderungen nachverfolgen

  • ✅ Nach Anfragehäufigkeit priorisieren

  • ✅ Täglicher Cron-Job nachts

Prozess:

  • ✅ Produktdaten abrufen (Name, Bilder, Features, Beschreibung)

  • ✅ Features über 3 Spalten ausbalancieren

  • ✅ Bildhöhen berechnen

  • ✅ HTML-Template rendern

  • ✅ PDF mit pdfkit generieren

  • ✅ Nur erste Seite behalten

Speicherung:

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

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

  • ✅ CDN: CloudFront-Distribution

Dieser hybride Ansatz balanciert Aktualität (On-Demand) mit Leistung (Batch-Vorgenerierung).


← Zurück zum Dokumentationsindex