1139 lines
34 KiB
Markdown
1139 lines
34 KiB
Markdown
---
|
||
title: FEEDGINE
|
||
description:
|
||
published: true
|
||
date: 2026-03-26T13:12:31.315Z
|
||
tags:
|
||
editor: markdown
|
||
dateCreated: 2026-03-26T13:12:31.315Z
|
||
---
|
||
|
||
# POAS Feed Enricher — Technische Dokumentation
|
||
|
||
**Projekt:** POAS Feed Enricher
|
||
**Sprache:** Python 3.11
|
||
**Stand:** März 2026
|
||
|
||
---
|
||
|
||
## Inhaltsverzeichnis
|
||
|
||
1. [Systemübersicht](#1-systemübersicht)
|
||
2. [Berechnungsformeln](#2-berechnungsformeln)
|
||
3. [Programmfunktionen](#3-programmfunktionen)
|
||
4. [Installation ohne Docker (Direktinstallation)](#4-installation-ohne-docker-direktinstallation)
|
||
5. [Installation mit Docker](#5-installation-mit-docker)
|
||
6. [Konfiguration](#6-konfiguration)
|
||
7. [Betrieb und Überwachung](#7-betrieb-und-überwachung)
|
||
8. [Systemarchitektur (Diagramme)](#8-systemarchitektur-diagramme)
|
||
|
||
---
|
||
|
||
## 1. Systemübersicht
|
||
|
||
Der **POAS Feed Enricher** ist eine automatisierte Datenpipeline, die täglich einen Google-Shopping-Produktfeed mit profitabilitätsbasierten Custom Labels anreichert und an Google Merchant Center (GMC) ausliefert.
|
||
|
||
Das Programm verbindet drei Datenquellen:
|
||
|
||
| Datenquelle | Inhalt |
|
||
|---|---|
|
||
| Shop-XML-Feed (ZIP-Download) | Produktdaten: ID, Titel, Preis, Bild-URL, Kategorie usw. |
|
||
| Google Ads API | Werbeleistung pro Produkt: Klicks, Conversions, Kosten, Umsatz |
|
||
| COGS-CSV (Einkaufspreise) | Netto-Einkaufspreis je Artikel-ID |
|
||
|
||
Das Ergebnis ist ein angereicherter XML-Feed, in dem jedes Produkt zwei zusätzliche Felder erhält:
|
||
|
||
- **`g:custom_label_0`** — Hauptgruppe (`Longtail`, `Core`, `Feeder`)
|
||
- **`g:custom_label_4`** — Untergruppe (z. B. `stars`, `potentials`, `underperformer`, `invisibles`, `nd_low_value` usw.)
|
||
|
||
Diese Labels werden in Google Ads als Segmentierungskriterien für Gebotsstrategien verwendet.
|
||
|
||
### Ablauf auf einen Blick
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["Shop-XML-Feed (ZIP)"] --> D["Pipeline"]
|
||
B["Google Ads API"] --> D
|
||
C["COGS-CSV (Einkaufspreise)"] --> D
|
||
D --> E["Angereicherter XML-Feed"]
|
||
E --> F["Google Merchant Center"]
|
||
E --> G["nginx Feed-Server"]
|
||
D --> H["Telegram-Benachrichtigung"]
|
||
```
|
||
|
||
---
|
||
|
||
## 2. Berechnungsformeln
|
||
|
||
### 2.1 POAS — Profit on Ad Spend
|
||
|
||
**POAS** (Profit on Ad Spend) ist die zentrale Kennzahl des Systems. Sie misst, wie profitabel der Werbeeinsatz für ein einzelnes Produkt ist.
|
||
|
||
#### Formel
|
||
|
||
$$\text{Marge} = \text{Conversion-Umsatz} - (\text{Einkaufspreis} \times \text{Anzahl Conversions})$$
|
||
|
||
$$\text{POAS} = \frac{\text{Marge}}{\text{Werbekosten}}$$
|
||
|
||
#### Schritt-für-Schritt-Erklärung
|
||
|
||
**Schritt 1 — Marge berechnen:**
|
||
|
||
```
|
||
Marge = conversions_value − (cogs_eur × conversions)
|
||
```
|
||
|
||
> *Dieses Beispiel dient ausschließlich zur Veranschaulichung der Formel.*
|
||
|
||
| Variable | Bedeutung | Quelle |
|
||
|---|---|---|
|
||
| `conversions_value` | Gesamtumsatz aller Conversions für dieses Produkt (in EUR) | Google Ads API |
|
||
| `cogs_eur` | Netto-Einkaufspreis pro Einheit (in EUR) | COGS-CSV |
|
||
| `conversions` | Anzahl der erzielten Käufe im Betrachtungszeitraum | Google Ads API |
|
||
|
||
**Schritt 2 — POAS berechnen:**
|
||
|
||
```
|
||
POAS = Marge / cost_eur
|
||
```
|
||
|
||
> *Dieses Beispiel dient ausschließlich zur Veranschaulichung der Formel.*
|
||
|
||
| Variable | Bedeutung | Quelle |
|
||
|---|---|---|
|
||
| `cost_eur` | Gesamte Werbeausgaben für dieses Produkt (in EUR) | Google Ads API (`cost_micros ÷ 1.000.000`) |
|
||
|
||
#### Rechenbeispiel — Verlustbringendes Produkt
|
||
|
||
| Größe | Wert |
|
||
|---|---|
|
||
| Werbekosten (30 Tage) | 10,00 € |
|
||
| Conversion-Umsatz | 120,00 € |
|
||
| Einkaufspreis pro Einheit | 50,00 € |
|
||
| Anzahl Conversions | 3 |
|
||
|
||
```
|
||
Marge = 120,00 € − (50,00 € × 3) = 120,00 € − 150,00 € = −30,00 €
|
||
POAS = −30,00 € / 10,00 € = −3,0
|
||
```
|
||
|
||
> *Beispielrechnung zur Veranschaulichung — keine realen Daten.*
|
||
|
||
In diesem Fall ist das Produkt **verlustbringend** (negativer POAS). Es wird der Gruppe **Feeder** zugeordnet.
|
||
|
||
#### Rechenbeispiel — Profitables Produkt
|
||
|
||
```
|
||
Werbekosten: 10,00 €
|
||
Umsatz: 120,00 €
|
||
EK-Preis: 30,00 €
|
||
Conversions: 2
|
||
|
||
Marge = 120,00 € − (30,00 € × 2) = 60,00 €
|
||
POAS = 60,00 € / 10,00 € = 6,0 → Gruppe: Core
|
||
```
|
||
|
||
> *Beispielrechnung zur Veranschaulichung — keine realen Daten.*
|
||
|
||
---
|
||
|
||
### 2.2 Produktgruppen-Zuordnung
|
||
|
||
Die Zuordnung zu Gruppen und Untergruppen erfolgt nach folgender Logik:
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
START(["Produkt"]) --> A{"Klicks = 0?"}
|
||
|
||
A -- Ja --> LT["Longtail"]
|
||
LT --> LT1{"Preis?"}
|
||
LT1 -- "unter 50 €" --> LT_L["nd_low_value"]
|
||
LT1 -- "50 € bis 200 €" --> LT_M["nd_mid_value"]
|
||
LT1 -- "über 200 €" --> LT_H["nd_high_value"]
|
||
|
||
A -- Nein --> B{"POAS >= 1,0?"}
|
||
|
||
B -- Ja --> CORE["Core"]
|
||
CORE --> C{"Klicks > 30 UND Conversions >= 2?"}
|
||
C -- Ja --> CORE_S["stars"]
|
||
C -- Nein --> CORE_P["potentials"]
|
||
|
||
B -- Nein --> FEEDER["Feeder"]
|
||
FEEDER --> D{"Klicks > 3?"}
|
||
D -- Ja --> F_U["underperformer"]
|
||
D -- Nein --> F_I["invisibles"]
|
||
```
|
||
|
||
#### Gruppe: Longtail
|
||
|
||
**Bedingung:** Das Produkt hatte im Betrachtungszeitraum **keine Klicks** (d. h. es läuft keine oder nur sehr geringe Werbung).
|
||
|
||
| Untergruppe | Bedingung | Bedeutung |
|
||
|---|---|---|
|
||
| `nd_low_value` | Preis < 50 € | Günstige Produkte ohne Werbehistorie |
|
||
| `nd_mid_value` | 50 € ≤ Preis ≤ 200 € | Mittlere Preisklasse ohne Werbehistorie |
|
||
| `nd_high_value` | Preis > 200 € | Hochpreisige Produkte ohne Werbehistorie |
|
||
|
||
**Strategie:** Geringe Gebote, organische Sichtbarkeit nutzen.
|
||
|
||
---
|
||
|
||
#### Gruppe: Core
|
||
|
||
**Bedingung:** Das Produkt hat Klicks **und** einen POAS ≥ 1,0 (profitabel).
|
||
|
||
| Untergruppe | Bedingung | Bedeutung |
|
||
|---|---|---|
|
||
| `stars` | Klicks > 30 **und** Conversions ≥ 2 | Bewiesene Top-Performer mit hohem Volumen |
|
||
| `potentials` | Alle anderen Core-Produkte | Profitabel, aber noch geringes Volumen |
|
||
|
||
**Strategie:** Gebote erhöhen, Budget aufstocken.
|
||
|
||
---
|
||
|
||
#### Gruppe: Feeder
|
||
|
||
**Bedingung:** Das Produkt hat Klicks, aber POAS < 1,0 (defizitär).
|
||
|
||
| Untergruppe | Bedingung | Bedeutung |
|
||
|---|---|---|
|
||
| `underperformer` | Klicks > 3 | Generiert Klicks, aber keine rentablen Conversions |
|
||
| `invisibles` | Klicks ≤ 3 | Kaum sichtbar, schwache Performance |
|
||
|
||
**Strategie:** Gebote senken, Anzeigen und Landingpages optimieren.
|
||
|
||
---
|
||
|
||
### 2.3 Schwellenwerte (Konfigurierbar)
|
||
|
||
| Parameter | Standardwert | Beschreibung |
|
||
|---|---|---|
|
||
| `POAS_CORE_THRESHOLD` | `1.0` | POAS-Grenzwert für Profitabilität |
|
||
| `LONGTAIL_LOW_MAX` | `50.0` | Preisgrenze Longtail „low" |
|
||
| `LONGTAIL_HIGH_MIN` | `200.0` | Preisgrenze Longtail „high" |
|
||
| `CORE_STARS_MIN_CLICKS` | `30` | Mindestklicks für „stars" |
|
||
| `CORE_STARS_MIN_CONVERSIONS` | `2` | Mindest-Conversions für „stars" |
|
||
| `FEEDER_UNDERPERFORMER_CLICKS` | `3` | Klick-Grenze für „underperformer" |
|
||
| `GOOGLE_ADS_LOOKBACK_DAYS` | `90` | Betrachtungszeitraum in Tagen |
|
||
|
||
---
|
||
|
||
## 3. Programmfunktionen
|
||
|
||
### 3.1 Scheduler (`Startup/scheduler.py`)
|
||
|
||
Der Scheduler ist der Haupteinstiegspunkt. Er läuft dauerhaft und startet die Pipeline täglich zur konfigurierten Uhrzeit.
|
||
|
||
**Funktionen:**
|
||
- Berechnung des nächsten Ausführungszeitpunkts aus `SCHEDULE_HOUR` und `SCHEDULE_MINUTE`
|
||
- Wartemodus in 60-Sekunden-Intervallen (CPU-schonend)
|
||
- Unterstützt `--run-now` (sofortige Ausführung), `--once` (einmalig, dann Beenden), `--lookback-days N`
|
||
- Sendet Telegram-Benachrichtigung beim Start des Schedulers
|
||
|
||
### 3.2 Watchdog / Pipeline-Ausführer (`Startup/watchdog.py`)
|
||
|
||
Der Watchdog orchestriert den gesamten Ausführungsablauf und implementiert Fehlerbehandlung.
|
||
|
||
**Funktionen:**
|
||
- Erstellt datierte Ein- und Ausgabeordner (`Input DD.MM.YYYY`, `Output DD.MM.YYYY`)
|
||
- Lädt ZIP-Feeds herunter und entpackt die XML-Dateien
|
||
- Startet die Pipeline als Subprozess (mit Timeout)
|
||
- Retry-Logik: bis zu `MAX_RETRIES` Versuche mit `RETRY_DELAY_SECONDS` Pause
|
||
- **Integritätsprüfungen** vor der Veröffentlichung (siehe 3.6)
|
||
- Telegram-Benachrichtigungen bei Erfolg und Fehler (inkl. Log-Anhang)
|
||
|
||
### 3.3 Google Ads Datenabruf (`poas_feed_enricher/app.py`)
|
||
|
||
Ruft Werbeleistungsdaten aller Produkte über die Google Ads API ab.
|
||
|
||
**Funktionen:**
|
||
- OAuth2-Authentifizierung via `google-ads.yaml`
|
||
- Automatische Erkennung und Iteration über alle Unterkonten eines MCC (Multi-Client-Account)
|
||
- GAQL-Abfrage auf `shopping_performance_view` für den konfigurierten Zeitraum
|
||
- Aggregation: Klicks, Impressionen, Kosten, Conversions, Umsatz je Produkt-ID
|
||
- Speicherung als `feed_performance.csv`
|
||
- Exponentielles Backoff bei transienten API-Fehlern (`UNAVAILABLE`, `DEADLINE_EXCEEDED`, `RESOURCE_EXHAUSTED`)
|
||
|
||
**GAQL-Abfrage (vereinfacht):**
|
||
|
||
```sql
|
||
SELECT
|
||
segments.product_item_id,
|
||
metrics.conversions_value,
|
||
metrics.conversions,
|
||
metrics.cost_micros,
|
||
metrics.impressions,
|
||
metrics.clicks
|
||
FROM shopping_performance_view
|
||
WHERE segments.date DURING LAST_90_DAYS
|
||
```
|
||
|
||
> *Dies ist kein direkt ausführbarer Code, sondern dient nur zur Veranschaulichung der Abfragestruktur.*
|
||
|
||
### 3.4 Feed-Transformation (`poas_feed_enricher/2_data_transform.py`)
|
||
|
||
Reichert den Shop-XML-Feed mit POAS-Labels an.
|
||
|
||
**Funktionen:**
|
||
- **Streaming-XML-Verarbeitung** mit `lxml.iterparse` (speichereffizient für Feeds > 200 MB)
|
||
- Laden der Einkaufspreise (COGS) aus CSV-Datei
|
||
- Laden der Ads-Performance-Daten aus `feed_performance.csv`
|
||
- Produktfilter: Entfernt Produkte ohne gültiges Bild und mit Preis ≤ 0
|
||
- POAS-Berechnung und Label-Zuordnung je Produkt
|
||
- Passthrough aller Originalfelder + Einfügen von `g:custom_label_0` und `g:custom_label_4`
|
||
- Generierung eines Audit-Reports (Anzahl Produkte, Trefferquoten, Label-Verteilung)
|
||
|
||
### 3.5 Label Engine (`poas_feed_enricher/processors/label_engine.py`)
|
||
|
||
Reine Berechnungsfunktion (keine Seiteneffekte) zur POAS-Labelberechnung.
|
||
|
||
**Eingaben:** Preis, Einkaufspreis, Ads-Performance-Daten
|
||
**Ausgabe:** `LabelResult` mit `custom_label_0` und `custom_label_4`
|
||
|
||
### 3.6 Integritätsprüfungen (`Startup/watchdog.py`)
|
||
|
||
Vor der Veröffentlichung des Feeds werden drei Sicherheitsprüfungen durchgeführt:
|
||
|
||
| Prüfung | Bedingung | Fehlerfall |
|
||
|---|---|---|
|
||
| Zero-Check | Ausgabe-Feed enthält > 0 Produkte | Feed ist leer |
|
||
| Integritäts-Check | Keine Produkte mit fehlender `g:id` | Strukturfehler im XML |
|
||
| Sanity-Check | Produktanzahl weicht < ±1000 vom Vorrun ab | Anomaler Produktverlust |
|
||
|
||
Schlägt eine Prüfung fehl, wird der Feed **nicht** veröffentlicht und eine Telegram-Warnung gesendet.
|
||
|
||
### 3.7 Feed-Veröffentlichung (`Startup/feed_publisher.py`)
|
||
|
||
Kopiert den fertigen Feed in das nginx-Ausgabeverzeichnis.
|
||
|
||
**Funktionen:**
|
||
- Kopiert den erzeugten XML-Feed nach `feed-serve/poas_feed.xml`
|
||
- Erstellt ein ZIP-Archiv als Download-Variante
|
||
- Aktualisiert `.last_feed_count` für den Sanity-Check des nächsten Runs
|
||
|
||
### 3.8 Ordner-Rotation (`Startup/cleanup.py`)
|
||
|
||
Verwaltet Speicherplatz durch automatische Rotation der datierten Ordner.
|
||
|
||
**Funktionen:**
|
||
- Listet alle `Input DD.MM.YYYY`- und `Output DD.MM.YYYY`-Ordner auf
|
||
- Behält die neuesten `KEEP_LAST_N` (Standard: 3) Ordner
|
||
- Löscht ältere Ordner automatisch
|
||
|
||
### 3.9 Telegram-Benachrichtigungen (`Startup/telegram_notifier.py`)
|
||
|
||
Sendet Status-Nachrichten an konfigurierte Telegram-Chats.
|
||
|
||
**Nachrichten-Typen:**
|
||
|
||
| Ereignis | Inhalt |
|
||
|---|---|
|
||
| Scheduler-Start | Programmstart, geplante Uhrzeit, Lookback-Tage |
|
||
| Pipeline gestartet | Ausführungsbeginn |
|
||
| **Erfolg** | Feed-Statistiken, Label-Verteilung, Feed-URL |
|
||
| **Fehler** | Fehlermeldung + vollständige Log-Datei als Anhang |
|
||
| Feed gesperrt | Grund der Sicherheitsprüfung (Zero/Integrität/Sanity) |
|
||
|
||
**Beispielformat einer Erfolgsmeldung:**
|
||
|
||
```
|
||
POAS Pipeline — SUCCESS
|
||
Datum: TT.MM.JJJJ HH:MM
|
||
Ausgabe: Output TT.MM.JJJJ
|
||
|
||
--- Feed-Zusammenfassung ---
|
||
Quell-XML: XXX.XXX
|
||
Endgültiger Feed: XXX.XXX
|
||
Ads-Treffer: XXX.XXX
|
||
|
||
--- Label-Verteilung ---
|
||
Longtail: XX.XXX (XX,X %)
|
||
nd_low_value: XX.XXX (XX,X %)
|
||
nd_mid_value: XX.XXX (XX,X %)
|
||
nd_high_value: XX.XXX (XX,X %)
|
||
Core: XX.XXX (XX,X %)
|
||
stars: XX.XXX (XX,X %)
|
||
potentials: XX.XXX (XX,X %)
|
||
Feeder: XX.XXX (XX,X %)
|
||
underperformer: XX.XXX (XX,X %)
|
||
invisibles: XX.XXX (XX,X %)
|
||
|
||
Feed veröffentlicht: OK
|
||
GMC Feed: https://[FEED-SERVER]/[CLIENT_PREFIX]/poas_feed.xml
|
||
```
|
||
|
||
> *Dies ist ein Beispielformat — Zahlen und URLs sind Platzhalter.*
|
||
|
||
---
|
||
|
||
## 4. Installation ohne Docker (Direktinstallation)
|
||
|
||
### Voraussetzungen
|
||
|
||
- Ubuntu 24.04 LTS (oder kompatible Linux-Distribution)
|
||
- Python 3.11
|
||
- nginx
|
||
- Git
|
||
|
||
### 4.1 System-Abhängigkeiten installieren
|
||
|
||
```bash
|
||
sudo apt-get update
|
||
sudo apt-get install -y \
|
||
python3.11 python3.11-venv python3-pip \
|
||
gcc libxml2-dev libxslt1-dev \
|
||
nginx git unzip curl
|
||
```
|
||
|
||
> *Beispielbefehl für Ubuntu 24.04 — Paketnamen können je nach Distribution abweichen.*
|
||
|
||
### 4.2 Projektdateien einrichten
|
||
|
||
```bash
|
||
# Ins gewünschte Verzeichnis wechseln
|
||
cd /opt
|
||
|
||
# Projektarchiv entpacken
|
||
tar -xzf poas-feed-enricher.tar.gz
|
||
mv poas-feed-enricher poas-pipeline
|
||
cd poas-pipeline
|
||
|
||
# Python Virtual Environment erstellen
|
||
python3.11 -m venv .venv
|
||
source .venv/bin/activate
|
||
|
||
# Python-Abhängigkeiten installieren
|
||
pip install --upgrade pip
|
||
pip install -r requirements.txt
|
||
```
|
||
|
||
> *Beispielbefehle zur Veranschaulichung — Pfade und Dateinamen sind an die eigene Umgebung anzupassen.*
|
||
|
||
### 4.3 Umgebungskonfiguration
|
||
|
||
```bash
|
||
# .env-Datei erstellen und befüllen
|
||
cp .env.example .env
|
||
nano .env
|
||
```
|
||
|
||
> *Beispielbefehl — der tatsächliche Editor und Dateiname können abweichen.*
|
||
|
||
Die folgenden Variablen **müssen** gesetzt werden (Details siehe [Abschnitt 6](#6-konfiguration)):
|
||
|
||
```dotenv
|
||
TELEGRAM_BOT_TOKEN=...
|
||
TELEGRAM_CHAT_IDS=...
|
||
SHOP_NAME=...
|
||
SHOP_URL=...
|
||
FEED_ZIPS=...
|
||
GOOGLE_ADS_DEVELOPER_TOKEN=...
|
||
GOOGLE_ADS_CLIENT_ID=...
|
||
GOOGLE_ADS_CLIENT_SECRET=...
|
||
GOOGLE_ADS_REFRESH_TOKEN=...
|
||
GOOGLE_ADS_LOGIN_CUSTOMER_ID=...
|
||
```
|
||
|
||
> *Platzhalter — alle Werte sind durch eigene Zugangsdaten zu ersetzen.*
|
||
|
||
### 4.4 Eingabedateien bereitstellen
|
||
|
||
```bash
|
||
# Einkaufspreise (COGS) — Pflichtdatei
|
||
cp /pfad/zu/einkaufspreise.csv Datei/einkaufspreise.csv
|
||
|
||
# Google Produktkategorie-Mapping (optional)
|
||
cp /pfad/zu/kategorien.csv Datei/google_product_category_mapping.csv
|
||
```
|
||
|
||
> *Pfade sind an die eigene Umgebung anzupassen.*
|
||
|
||
**Format der COGS-Datei** (semikolon-getrennt, **ohne** Kopfzeile):
|
||
|
||
```
|
||
ARTIKEL-001;12.50
|
||
ARTIKEL-002;0.00
|
||
ARTIKEL-003;89.99
|
||
```
|
||
|
||
> *Beispieldaten zur Veranschaulichung des Formats — keine realen Artikel.*
|
||
|
||
**Format der Kategorie-Datei** (komma-getrennt, **mit** Kopfzeile):
|
||
|
||
```
|
||
id,google_product_category
|
||
ARTIKEL-001,8227
|
||
ARTIKEL-002,3025
|
||
```
|
||
|
||
> *Beispieldaten zur Veranschaulichung des Formats — keine realen Artikel.*
|
||
|
||
### 4.5 Google Ads API konfigurieren
|
||
|
||
Die Datei `poas_feed_enricher/google-ads.yaml` wird beim Docker-Start automatisch generiert. Bei Direktinstallation muss sie manuell erstellt werden:
|
||
|
||
```yaml
|
||
developer_token: "[IHR_DEVELOPER_TOKEN]"
|
||
client_id: "[IHR_CLIENT_ID]"
|
||
client_secret: "[IHR_CLIENT_SECRET]"
|
||
refresh_token: "[IHR_REFRESH_TOKEN]"
|
||
login_customer_id: "[IHR_MCC_ID]"
|
||
use_proto_plus: true
|
||
```
|
||
|
||
> *Alle Felder in eckigen Klammern sind Platzhalter und müssen durch echte Google Ads API-Zugangsdaten ersetzt werden.*
|
||
|
||
### 4.6 nginx konfigurieren
|
||
|
||
```bash
|
||
# nginx-Konfiguration einspielen
|
||
sudo cp nginx.client.conf /etc/nginx/sites-available/poas-feed
|
||
sudo ln -s /etc/nginx/sites-available/poas-feed /etc/nginx/sites-enabled/
|
||
sudo nginx -t
|
||
sudo systemctl reload nginx
|
||
```
|
||
|
||
> *Beispielbefehle für Ubuntu mit nginx — an die eigene Serverumgebung anpassen.*
|
||
|
||
Ausgabeverzeichnis für den Feed erstellen:
|
||
|
||
```bash
|
||
mkdir -p feed-serve
|
||
sudo chown -R $USER:www-data feed-serve
|
||
sudo chmod -R 755 feed-serve
|
||
```
|
||
|
||
> *Beispielbefehle — Benutzer und Berechtigungen sind je nach Serversetup anzupassen.*
|
||
|
||
### 4.7 Als Systemd-Service einrichten
|
||
|
||
Servicedatei unter `/etc/systemd/system/poas-pipeline.service` anlegen:
|
||
|
||
```ini
|
||
[Unit]
|
||
Description=POAS Feed Enricher Pipeline
|
||
After=network.target
|
||
|
||
[Service]
|
||
Type=simple
|
||
User=[SYSTEMBENUTZER]
|
||
WorkingDirectory=/opt/poas-pipeline
|
||
ExecStart=/opt/poas-pipeline/.venv/bin/python Startup/scheduler.py
|
||
Restart=on-failure
|
||
RestartSec=30
|
||
EnvironmentFile=/opt/poas-pipeline/.env
|
||
StandardOutput=journal
|
||
StandardError=journal
|
||
|
||
[Install]
|
||
WantedBy=multi-user.target
|
||
```
|
||
|
||
> *Vorlage für eine systemd-Servicedatei — Pfade und Benutzername sind anzupassen.*
|
||
|
||
```bash
|
||
# Service aktivieren und starten
|
||
sudo systemctl daemon-reload
|
||
sudo systemctl enable poas-pipeline
|
||
sudo systemctl start poas-pipeline
|
||
|
||
# Status prüfen
|
||
sudo systemctl status poas-pipeline
|
||
|
||
# Logs verfolgen
|
||
sudo journalctl -fu poas-pipeline
|
||
```
|
||
|
||
> *Beispielbefehle zur Veranschaulichung — Servicename ist ggf. anzupassen.*
|
||
|
||
### 4.8 Manuelle Testausführung
|
||
|
||
```bash
|
||
# Virtual Environment aktivieren
|
||
source .venv/bin/activate
|
||
|
||
# Einmalige sofortige Ausführung (ohne auf geplante Zeit zu warten)
|
||
python Startup/scheduler.py --once
|
||
|
||
# Sofort ausführen und danach weiter nach Zeitplan laufen
|
||
python Startup/scheduler.py --run-now
|
||
|
||
# Mit abweichendem Lookback-Zeitraum
|
||
python Startup/scheduler.py --once --lookback-days 60
|
||
```
|
||
|
||
> *Beispielbefehle zur Veranschaulichung der verfügbaren Startoptionen.*
|
||
|
||
---
|
||
|
||
## 5. Installation mit Docker
|
||
|
||
### Voraussetzungen
|
||
|
||
- Docker Engine ≥ 24.0
|
||
- Docker Compose Plugin ≥ 2.20
|
||
- Mindestens 4 GB freier Speicher (Feeds können mehrere hundert MB groß sein)
|
||
|
||
### 5.1 Docker und Docker Compose installieren
|
||
|
||
```bash
|
||
# Docker installieren (Ubuntu)
|
||
curl -fsSL https://get.docker.com | sudo sh
|
||
sudo usermod -aG docker $USER
|
||
newgrp docker
|
||
|
||
# Version prüfen
|
||
docker --version
|
||
docker compose version
|
||
```
|
||
|
||
> *Installationsbefehle für Ubuntu — für andere Systeme die offizielle Docker-Dokumentation verwenden.*
|
||
|
||
### 5.2 Projektdateien einrichten
|
||
|
||
```bash
|
||
cd /opt
|
||
tar -xzf poas-feed-enricher.tar.gz
|
||
mv poas-feed-enricher poas-pipeline
|
||
cd poas-pipeline
|
||
```
|
||
|
||
> *Beispielbefehle — Dateiname und Zielverzeichnis sind anzupassen.*
|
||
|
||
### 5.3 Docker Compose konfigurieren
|
||
|
||
```bash
|
||
# Vorlage kopieren
|
||
cp docker-compose.sample.yaml docker-compose.yaml
|
||
|
||
# Konfiguration bearbeiten
|
||
nano docker-compose.yaml
|
||
```
|
||
|
||
> *Beispielbefehle — alle Platzhalter in der kopierten Datei anschließend mit echten Werten befüllen.*
|
||
|
||
Alle Platzhalter `[WERT]` müssen durch eigene Werte ersetzt werden. Vollständige Konfigurationsstruktur:
|
||
|
||
```yaml
|
||
services:
|
||
pipeline:
|
||
build: .
|
||
container_name: poas-pipeline
|
||
restart: unless-stopped
|
||
environment:
|
||
# Telegram
|
||
TELEGRAM_BOT_TOKEN: "[BOT_TOKEN]"
|
||
TELEGRAM_CHAT_IDS: "[CHAT_ID_1],[CHAT_ID_2]"
|
||
|
||
# Shop-Informationen
|
||
SHOP_NAME: "[SHOPNAME]"
|
||
SHOP_URL: "https://[SHOP-DOMAIN]"
|
||
SHOP_DESCRIPTION: "[KURZBESCHREIBUNG]"
|
||
|
||
# Feed-ZIP-URLs (Format: URL|ZIP-Name|XML-Name, mehrere durch ; getrennt)
|
||
FEED_ZIPS: "https://[SHOP-DOMAIN]/export/feed1.zip|feed1.zip|feed1.xml"
|
||
|
||
# Google Ads API
|
||
GOOGLE_ADS_DEVELOPER_TOKEN: "[DEVELOPER_TOKEN]"
|
||
GOOGLE_ADS_CLIENT_ID: "[CLIENT_ID]"
|
||
GOOGLE_ADS_CLIENT_SECRET: "[CLIENT_SECRET]"
|
||
GOOGLE_ADS_REFRESH_TOKEN: "[REFRESH_TOKEN]"
|
||
GOOGLE_ADS_LOGIN_CUSTOMER_ID: "[MCC_KONTO_ID]"
|
||
|
||
# Zeitplan (UTC)
|
||
SCHEDULE_HOUR: "0"
|
||
SCHEDULE_MINUTE: "0"
|
||
|
||
# Lookback-Zeitraum
|
||
LOOKBACK_DAYS: "90"
|
||
|
||
# Fehlerbehandlung
|
||
MAX_RETRIES: "3"
|
||
RETRY_DELAY_SECONDS: "60"
|
||
KEEP_LAST_N: "3"
|
||
|
||
# Feed-Veröffentlichung
|
||
FEED_PUBLISH_ENABLED: "true"
|
||
CLIENT_PREFIX: "[SHOP-KÜRZEL]"
|
||
|
||
volumes:
|
||
- poas-logs:/app/Startup/logs
|
||
- feed-serve:/app/feed-serve
|
||
|
||
nginx:
|
||
image: nginx:alpine
|
||
container_name: poas-nginx
|
||
restart: unless-stopped
|
||
ports:
|
||
- "8525:80"
|
||
volumes:
|
||
- feed-serve:/usr/share/nginx/feeds:ro
|
||
- ./nginx.client.conf:/etc/nginx/conf.d/default.conf:ro
|
||
depends_on:
|
||
- pipeline
|
||
|
||
volumes:
|
||
poas-logs:
|
||
feed-serve:
|
||
```
|
||
|
||
> *Vorlage für docker-compose.yaml — alle Werte in eckigen Klammern sind Platzhalter und müssen ersetzt werden.*
|
||
|
||
### 5.4 Eingabedateien bereitstellen
|
||
|
||
Die Eingabedateien müssen **vor dem ersten Start** in den `Datei/`-Ordner gelegt werden, da dieser in das Docker-Image gebacken wird:
|
||
|
||
```bash
|
||
# Einkaufspreise (Pflicht)
|
||
cp /pfad/zu/einkaufspreise.csv Datei/einkaufspreise.csv
|
||
|
||
# Kategorie-Mapping (optional)
|
||
cp /pfad/zu/kategorien.csv Datei/google_product_category_mapping.csv
|
||
```
|
||
|
||
> *Pfade sind an die eigene Umgebung anzupassen.*
|
||
|
||
> **Hinweis:** Nach Änderungen an Dateien im `Datei/`-Ordner muss das Docker-Image neu gebaut werden (`docker compose build`).
|
||
|
||
### 5.5 Container starten
|
||
|
||
```bash
|
||
# Image bauen und Container starten
|
||
docker compose up -d
|
||
|
||
# Logs verfolgen
|
||
docker compose logs -f pipeline
|
||
|
||
# Status prüfen
|
||
docker compose ps
|
||
```
|
||
|
||
> *Standardbefehle für Docker Compose v2.*
|
||
|
||
### 5.6 Manuelle Ausführung im Container
|
||
|
||
```bash
|
||
# Sofortige einmalige Ausführung
|
||
docker compose exec pipeline python Startup/scheduler.py --once
|
||
|
||
# Sofort ausführen (Scheduler läuft weiter)
|
||
docker compose exec pipeline python Startup/scheduler.py --run-now
|
||
|
||
# Mit angepasstem Lookback-Zeitraum
|
||
docker compose exec pipeline python Startup/scheduler.py --once --lookback-days 60
|
||
```
|
||
|
||
> *Beispielbefehle zur Veranschaulichung der manuellen Ausführungsoptionen.*
|
||
|
||
### 5.7 Container-Verwaltung
|
||
|
||
```bash
|
||
# Container stoppen
|
||
docker compose stop
|
||
|
||
# Container stoppen und entfernen
|
||
docker compose down
|
||
|
||
# Container stoppen, Volumes löschen
|
||
# Achtung: Logs und Feed-Daten gehen dabei verloren!
|
||
docker compose down -v
|
||
|
||
# Image neu bauen (nach Code- oder Dateiänderungen)
|
||
docker compose build --no-cache
|
||
docker compose up -d
|
||
```
|
||
|
||
> *Beispielbefehle für gängige Container-Verwaltungsaufgaben.*
|
||
|
||
### 5.8 Architektur mit Docker
|
||
|
||
```mermaid
|
||
graph TB
|
||
subgraph Host["Server"]
|
||
subgraph DockerNetwork["Docker-Netzwerk"]
|
||
PIPELINE["poas-pipeline\nPython 3.11-slim"]
|
||
NGINX["poas-nginx\nnginx:alpine, Port 8525"]
|
||
end
|
||
subgraph Volumes["Docker Volumes"]
|
||
V1[("poas-logs")]
|
||
V2[("feed-serve")]
|
||
end
|
||
end
|
||
|
||
INTERNET["Internet\nGoogle Ads API / Telegram / Shop-ZIP"]
|
||
GMC["Google Merchant Center"]
|
||
TELEGRAM["Telegram"]
|
||
|
||
PIPELINE --> V1
|
||
PIPELINE --> V2
|
||
NGINX --> V2
|
||
PIPELINE --> INTERNET
|
||
NGINX -->|"Port 8525"| GMC
|
||
PIPELINE --> TELEGRAM
|
||
```
|
||
|
||
---
|
||
|
||
## 6. Konfiguration
|
||
|
||
Alle Konfigurationsparameter werden über die `.env`-Datei (Direktinstallation) oder die `environment`-Sektion in `docker-compose.yaml` gesetzt.
|
||
|
||
### 6.1 Pflichtparameter
|
||
|
||
| Variable | Beispielformat | Beschreibung |
|
||
|---|---|---|
|
||
| `TELEGRAM_BOT_TOKEN` | `[TOKEN]:AAG...` | Token des Telegram-Bots |
|
||
| `TELEGRAM_CHAT_IDS` | `[ID1],[ID2]` | Chat-IDs (kommagetrennt) |
|
||
| `SHOP_NAME` | `[Shopname]` | Name des Shops (für Feed-Titel) |
|
||
| `SHOP_URL` | `https://[shop-domain]` | Shop-URL (für Feed-Link) |
|
||
| `SHOP_DESCRIPTION` | `[Beschreibung]` | Shop-Beschreibung |
|
||
| `FEED_ZIPS` | (siehe Format unten) | ZIP-Feed-URLs mit Dateinamen |
|
||
| `GOOGLE_ADS_DEVELOPER_TOKEN` | `[TOKEN]` | Google Ads Developer Token |
|
||
| `GOOGLE_ADS_CLIENT_ID` | `[ID].apps.googleusercontent.com` | OAuth2 Client-ID |
|
||
| `GOOGLE_ADS_CLIENT_SECRET` | `GOCSPX-[...]` | OAuth2 Client-Secret |
|
||
| `GOOGLE_ADS_REFRESH_TOKEN` | `1//04[...]` | OAuth2 Refresh-Token (langlebig) |
|
||
| `GOOGLE_ADS_LOGIN_CUSTOMER_ID` | `[10-stellige ID]` | Google Ads MCC-Konto-ID |
|
||
|
||
**Format von `FEED_ZIPS`:**
|
||
|
||
Mehrere ZIP-Feeds werden durch `;` getrennt. Jeder Feed besteht aus drei durch `|` getrennten Teilen:
|
||
|
||
```
|
||
URL|ZIP-Dateiname|XML-Dateiname
|
||
```
|
||
|
||
> *Formatbeschreibung — keine funktionsfähige Konfiguration.*
|
||
|
||
Beispiel mit zwei Feeds:
|
||
|
||
```dotenv
|
||
FEED_ZIPS=https://[SHOP]/export/feed1.zip|feed1.zip|feed1.xml;https://[SHOP]/export/feed2.zip|feed2.zip|feed2.xml
|
||
```
|
||
|
||
> *Beispielformat — Platzhalter durch eigene URLs und Dateinamen ersetzen.*
|
||
|
||
### 6.2 Optionale Parameter
|
||
|
||
| Variable | Standard | Beschreibung |
|
||
|---|---|---|
|
||
| `SCHEDULE_HOUR` | `0` | Stunde der täglichen Ausführung (UTC) |
|
||
| `SCHEDULE_MINUTE` | `0` | Minute der täglichen Ausführung (UTC) |
|
||
| `LOOKBACK_DAYS` | `90` | Google Ads Betrachtungszeitraum (Tage) |
|
||
| `MAX_RETRIES` | `3` | Maximale Wiederholungsversuche bei Fehler |
|
||
| `RETRY_DELAY_SECONDS` | `60` | Wartezeit zwischen Versuchen (Sekunden) |
|
||
| `KEEP_LAST_N` | `3` | Anzahl zu behaltender datierter Ordner |
|
||
| `CLIENT_PREFIX` | `[shop-kuerzel]` | Präfix für Feed-URL-Pfad auf nginx |
|
||
| `FEED_PUBLISH_ENABLED` | `false` | Feed nach nginx veröffentlichen |
|
||
| `FEED_SERVER_PORT` | `8080` | Lokaler Feed-Server-Port |
|
||
| `FILTER_OUT_OF_STOCK` | `false` | Nicht vorrätige Produkte ausfiltern |
|
||
|
||
---
|
||
|
||
## 7. Betrieb und Überwachung
|
||
|
||
### 7.1 Täglicher Normalbetrieb
|
||
|
||
Im Normalbetrieb ist **kein manueller Eingriff** erforderlich. Der Scheduler startet täglich zur konfigurierten Uhrzeit automatisch:
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant S as Scheduler
|
||
participant W as Watchdog
|
||
participant API as Google Ads API
|
||
participant ZIP as Shop-ZIP-Server
|
||
participant N as nginx
|
||
participant TG as Telegram
|
||
|
||
S->>TG: Scheduler gestartet
|
||
Note over S: Wartet bis SCHEDULE_HOUR:SCHEDULE_MINUTE
|
||
|
||
loop Täglich
|
||
S->>W: execute_pipeline()
|
||
W->>ZIP: ZIP-Feeds herunterladen
|
||
ZIP-->>W: XML-Dateien
|
||
W->>API: Google Ads Daten abrufen
|
||
API-->>W: feed_performance.csv
|
||
W->>W: XML anreichern (POAS-Labels)
|
||
W->>W: Integritätsprüfungen
|
||
W->>N: Feed veröffentlichen
|
||
W->>W: Alte Ordner löschen
|
||
W->>TG: Erfolgsmeldung mit Statistiken
|
||
end
|
||
```
|
||
|
||
### 7.2 Logs einsehen
|
||
|
||
**Docker:**
|
||
|
||
```bash
|
||
# Live-Logs des Pipeline-Containers
|
||
docker compose logs -f pipeline
|
||
|
||
# Spezifische Log-Datei lesen
|
||
docker compose exec pipeline cat Startup/logs/pipeline_run_JJJJMMTT_HHMMSS.txt
|
||
|
||
# Alle Log-Dateien auflisten
|
||
docker compose exec pipeline ls -lht Startup/logs/
|
||
```
|
||
|
||
> *Beispielbefehle — Dateinamen mit echtem Datum und Uhrzeit ersetzen.*
|
||
|
||
**Direktinstallation:**
|
||
|
||
```bash
|
||
# Systemd-Journal
|
||
sudo journalctl -fu poas-pipeline
|
||
|
||
# Einzelne Log-Datei
|
||
cat Startup/logs/pipeline_run_JJJJMMTT_HHMMSS.txt
|
||
```
|
||
|
||
> *Dateinamen mit echtem Datum und Uhrzeit ersetzen.*
|
||
|
||
### 7.3 Feed-Status prüfen
|
||
|
||
```bash
|
||
# Aktuellen Feed ansehen
|
||
ls -lh feed-serve/
|
||
|
||
# Produktanzahl im Feed zählen
|
||
grep -c '<item>' feed-serve/poas_feed.xml
|
||
|
||
# Letzte bekannte Produktanzahl
|
||
cat feed-serve/.last_feed_count
|
||
```
|
||
|
||
> *Beispielbefehle für die Direktinstallation.*
|
||
|
||
**Docker:**
|
||
|
||
```bash
|
||
docker compose exec nginx ls -lh /usr/share/nginx/feeds/
|
||
```
|
||
|
||
### 7.4 Sofortige manuelle Ausführung
|
||
|
||
```bash
|
||
# Docker
|
||
docker compose exec pipeline python Startup/scheduler.py --once
|
||
|
||
# Direktinstallation
|
||
source .venv/bin/activate
|
||
python Startup/scheduler.py --once
|
||
```
|
||
|
||
> *Beispielbefehle für beide Betriebsarten.*
|
||
|
||
### 7.5 Feed-URL-Struktur
|
||
|
||
Nach erfolgreicher Ausführung und Veröffentlichung ist der Feed über nginx erreichbar:
|
||
|
||
| Endpunkt | Beschreibung |
|
||
|---|---|
|
||
| `http://[SERVER-IP]:[PORT]/poas_feed.xml` | Aktueller GMC-Feed (XML) |
|
||
| `http://[SERVER-IP]:[PORT]/download` | Feed als ZIP-Download |
|
||
|
||
Bei Verwendung eines zentralen Reverse-Proxies:
|
||
|
||
```
|
||
https://[FEED-SERVER]/[CLIENT_PREFIX]/poas_feed.xml
|
||
```
|
||
|
||
> *Platzhalter in eckigen Klammern durch die eigene Serveradresse und den konfigurierten Präfix ersetzen.*
|
||
|
||
### 7.6 Häufige Fehler und Lösungen
|
||
|
||
| Fehler | Ursache | Lösung |
|
||
|---|---|---|
|
||
| `google-ads.yaml nicht gefunden` | Credentials-Datei fehlt | `entrypoint.sh` ausführen oder Datei manuell erstellen |
|
||
| `Invalid OAuth2 credentials` | Refresh-Token abgelaufen | Neuen Refresh-Token via OAuth-Flow generieren |
|
||
| `ZIP download failed` | Shop-Server nicht erreichbar | Netzwerk prüfen, URL in `FEED_ZIPS` prüfen |
|
||
| Feed leer (0 Produkte) | Alle Produkte ausgefiltert | Filter-Logik und Eingabe-XML prüfen |
|
||
| Sanity-Check fehlgeschlagen | Produktzahl ändert sich um > 1000 | `.last_feed_count` prüfen, ggf. zurücksetzen |
|
||
| `RESOURCE_EXHAUSTED` (Google Ads) | API-Kontingent erschöpft | `LOOKBACK_DAYS` reduzieren oder nächsten Tag abwarten |
|
||
|
||
---
|
||
|
||
## 8. Systemarchitektur (Diagramme)
|
||
|
||
### 8.1 Komponentenübersicht
|
||
|
||
```mermaid
|
||
graph TD
|
||
subgraph INPUT["Eingabedaten"]
|
||
ZIP["Shop-XML-Feeds (ZIP)"]
|
||
COGS["COGS-CSV (Einkaufspreise)"]
|
||
ADS["Google Ads API"]
|
||
end
|
||
|
||
subgraph PIPELINE["Pipeline (Python 3.11)"]
|
||
SCHED["scheduler.py — Zeitsteuerung"]
|
||
WATCH["watchdog.py — Orchestrator"]
|
||
APP["app.py — Ads-Datenabruf"]
|
||
TRANS["2_data_transform.py — XML-Anreicherung"]
|
||
LABEL["label_engine.py — POAS-Berechnung"]
|
||
PUB["feed_publisher.py — Veröffentlichung"]
|
||
CLEAN["cleanup.py — Speicherverwaltung"]
|
||
TG["telegram_notifier.py — Benachrichtigung"]
|
||
end
|
||
|
||
subgraph OUTPUT["Ausgabe"]
|
||
FEED["poas_feed.xml (angereicherter GMC-Feed)"]
|
||
NGINX["nginx — Feed-Server"]
|
||
TELEGRAM["Telegram — Benachrichtigung"]
|
||
end
|
||
|
||
ZIP --> WATCH
|
||
COGS --> TRANS
|
||
ADS --> APP
|
||
APP --> TRANS
|
||
SCHED --> WATCH
|
||
WATCH --> APP
|
||
WATCH --> TRANS
|
||
TRANS --> LABEL
|
||
LABEL --> TRANS
|
||
WATCH --> PUB
|
||
PUB --> FEED
|
||
FEED --> NGINX
|
||
WATCH --> CLEAN
|
||
WATCH --> TG
|
||
TG --> TELEGRAM
|
||
```
|
||
|
||
### 8.2 Dateistruktur
|
||
|
||
```
|
||
poas-pipeline/
|
||
├── .env ← Konfiguration & Credentials
|
||
├── Dockerfile ← Docker-Image-Definition
|
||
├── docker-compose.yaml ← Docker-Deployment
|
||
├── docker-compose.sample.yaml ← Vorlage für docker-compose.yaml
|
||
├── entrypoint.sh ← Docker-Startskript (generiert google-ads.yaml)
|
||
├── nginx.client.conf ← nginx-Konfiguration
|
||
├── requirements.txt ← Python-Abhängigkeiten
|
||
├── setup.sh ← Direktinstallations-Skript (Ubuntu)
|
||
│
|
||
├── poas_feed_enricher/ ← Kern-Pipeline
|
||
│ ├── config.py ← Schwellenwerte & Konstanten
|
||
│ ├── run.py ← Einstiegspunkt (Schritt 1 + 2)
|
||
│ ├── app.py ← Google Ads API-Abruf
|
||
│ ├── 2_data_transform.py ← XML-Anreicherung
|
||
│ ├── google-ads.yaml ← Google Ads Credentials (auto-generiert)
|
||
│ ├── loaders/
|
||
│ │ ├── ads_loader.py ← CSV-basierter Ads-Loader
|
||
│ │ ├── cogs_loader.py ← COGS-CSV-Loader
|
||
│ │ └── google_ads_api.py ← Live Google Ads API-Client
|
||
│ └── processors/
|
||
│ ├── label_engine.py ← POAS-Labelberechnung
|
||
│ └── gmc_validator.py ← GMC XML-Validierung
|
||
│
|
||
├── Startup/ ← Orchestrierungsschicht
|
||
│ ├── config.py ← Zentrale .env-Konfiguration
|
||
│ ├── scheduler.py ← Täglicher Zeitplaner
|
||
│ ├── watchdog.py ← Pipeline-Ausführer + Retry
|
||
│ ├── cleanup.py ← Ordner-Erstellung & -Rotation
|
||
│ ├── feed_publisher.py ← Feed nach nginx kopieren
|
||
│ ├── telegram_notifier.py ← Telegram-Benachrichtigungen
|
||
│ └── logs/ ← Ausführungs-Logs
|
||
│
|
||
├── Datei/ ← Statische Eingabedaten
|
||
│ ├── einkaufspreise.csv ← Einkaufspreise (COGS)
|
||
│ └── google_product_category_mapping.csv
|
||
│
|
||
└── feed-serve/ ← Ausgabe (nginx-Verzeichnis)
|
||
├── poas_feed.xml ← Aktueller GMC-Feed
|
||
├── poas_feed.zip ← Feed als ZIP-Download
|
||
└── .last_feed_count ← Sanity-Check-Baseline
|
||
```
|
||
|
||
> *Schematische Dateistruktur zur Orientierung — tatsächliche Dateinamen können abweichen.*
|
||
|
||
### 8.3 Pipeline-Sequenzdiagramm (detailliert)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant SCH as Scheduler
|
||
participant WDG as Watchdog
|
||
participant CLN as Cleanup
|
||
participant APP as Ads-Abruf
|
||
participant TRN as XML-Anreicherung
|
||
participant PUB as Feed-Publisher
|
||
participant TG as Telegram
|
||
|
||
SCH->>TG: Scheduler gestartet
|
||
Note over SCH: Wartet bis SCHEDULE_HOUR:SCHEDULE_MINUTE (UTC)
|
||
|
||
SCH->>WDG: execute_pipeline(lookback_days)
|
||
WDG->>TG: Pipeline gestartet
|
||
|
||
WDG->>CLN: Datierten Input-Ordner erstellen
|
||
CLN->>CLN: ZIP-Feeds herunterladen
|
||
CLN->>CLN: XMLs entpacken und umbenennen
|
||
CLN-->>WDG: Liste der XML-Dateien
|
||
|
||
WDG->>APP: Google Ads Daten abrufen
|
||
APP->>APP: Credentials laden
|
||
APP->>APP: MCC-Unterkonten auflisten
|
||
loop Je Unterkonto
|
||
APP->>APP: GAQL-Abfrage ausführen
|
||
end
|
||
APP->>APP: feed_performance.csv schreiben
|
||
APP-->>WDG: Fertig
|
||
|
||
WDG->>TRN: XML anreichern
|
||
TRN->>TRN: COGS-CSV laden
|
||
TRN->>TRN: Performance-Daten laden
|
||
loop Je Produkt (Streaming)
|
||
TRN->>TRN: Preis und Bild-URL prüfen
|
||
TRN->>TRN: POAS berechnen
|
||
TRN->>TRN: Label zuweisen
|
||
TRN->>TRN: XML mit Labels schreiben
|
||
end
|
||
TRN-->>WDG: poas_feed.xml + Statistiken
|
||
|
||
WDG->>WDG: Integritätsprüfungen (Zero / Integrität / Sanity)
|
||
|
||
alt Alle Prüfungen bestanden
|
||
WDG->>PUB: Feed veröffentlichen
|
||
PUB->>PUB: XML nach feed-serve/ kopieren
|
||
PUB->>PUB: ZIP-Archiv erstellen
|
||
PUB-->>WDG: OK
|
||
WDG->>CLN: Alte Ordner rotieren
|
||
WDG->>TG: Erfolgsmeldung mit Statistiken
|
||
else Prüfung fehlgeschlagen
|
||
WDG->>TG: Warnung: Feed gesperrt (Grund)
|
||
end
|
||
```
|
||
|
||
### 8.4 POAS-Entscheidungsbaum (Labelzuordnung)
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
P(["Produkt"]) --> F1{"Preis = 0 oder kein Bild?"}
|
||
F1 -- Ja --> OUT["Verworfen — nicht im Feed"]
|
||
F1 -- Nein --> F2{"Klicks = 0?"}
|
||
|
||
F2 -- Ja --> LT["Longtail — custom_label_0"]
|
||
LT --> F3{"Preis?"}
|
||
F3 -- "unter 50 EUR" --> LL["nd_low_value — custom_label_4"]
|
||
F3 -- "50 bis 200 EUR" --> LM["nd_mid_value — custom_label_4"]
|
||
F3 -- "über 200 EUR" --> LH["nd_high_value — custom_label_4"]
|
||
|
||
F2 -- Nein --> CALC["POAS berechnen"]
|
||
CALC --> F4{"POAS >= 1,0?"}
|
||
|
||
F4 -- Ja --> CORE["Core — custom_label_0"]
|
||
CORE --> F5{"Klicks > 30 UND Conversions >= 2?"}
|
||
F5 -- Ja --> CS["stars — custom_label_4"]
|
||
F5 -- Nein --> CP["potentials — custom_label_4"]
|
||
|
||
F4 -- Nein --> FEEDER["Feeder — custom_label_0"]
|
||
FEEDER --> F6{"Klicks > 3?"}
|
||
F6 -- Ja --> FU["underperformer — custom_label_4"]
|
||
F6 -- Nein --> FI["invisibles — custom_label_4"]
|
||
```
|
||
|
||
---
|
||
|
||
*Dokumentation für POAS Feed Enricher — alle Namen, URLs, IDs und Zahlen in dieser Dokumentation sind Platzhalter und dienen ausschließlich der Veranschaulichung.*
|