fix: QC-Ratings aus 7 älteren Artikeln entfernt (CHECK 10)
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build Joomla article for children's games and POST to brettspiel-news.de."""
|
||||
import json, base64, subprocess, sys, os
|
||||
import urllib.request, urllib.error
|
||||
|
||||
# Load parsed deals
|
||||
with open("/tmp/kinderspiele_data.json") as f:
|
||||
data = json.load(f)
|
||||
|
||||
deals = data["deals"]
|
||||
age_groups = data["age_groups"]
|
||||
|
||||
# Joomla config
|
||||
JOOMLA_TOKEN = "sha256:794:cc64e486eddbb2e5a96a28be897034924f66f2722db0be3baab408e6b806e2b4"
|
||||
API_BASE = "https://www.brettspiel-news.de/api/index.php/v1"
|
||||
HEADERS = {
|
||||
"Accept": "application/vnd.api+json",
|
||||
"Content-Type": "application/json",
|
||||
"X-Joomla-Token": JOOMLA_TOKEN,
|
||||
}
|
||||
|
||||
def api_post(endpoint, payload):
|
||||
url = f"{API_BASE}{endpoint}"
|
||||
data = json.dumps(payload).encode("utf-8")
|
||||
req = urllib.request.Request(url, data=data, headers=HEADERS, method="POST")
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read())
|
||||
except urllib.error.HTTPError as e:
|
||||
body = e.read().decode()[:500]
|
||||
print(f"HTTP {e.code}: {body}", file=sys.stderr)
|
||||
raise
|
||||
|
||||
def api_patch(endpoint, payload):
|
||||
url = f"{API_BASE}{endpoint}"
|
||||
data = json.dumps(payload).encode("utf-8")
|
||||
req = urllib.request.Request(url, data=data, headers=HEADERS, method="PATCH")
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=30) as resp:
|
||||
return json.loads(resp.read())
|
||||
except urllib.error.HTTPError as e:
|
||||
body = e.read().decode()[:500]
|
||||
print(f"HTTP {e.code}: {body}", file=sys.stderr)
|
||||
raise
|
||||
|
||||
# Step 1: Upload intro image to Joomla Media
|
||||
print("Uploading intro image...")
|
||||
with open("/tmp/monsterjaeger.jpg", "rb") as f:
|
||||
img_b64 = base64.b64encode(f.read()).decode()
|
||||
|
||||
media_result = api_post("/media/files", {
|
||||
"path": "images/Spiele/2026/monsterjaeger_kinderspiele_2026-06-21.jpg",
|
||||
"content": img_b64,
|
||||
})
|
||||
print(f"Media upload: {media_result.get('data', {}).get('id', 'OK')}")
|
||||
|
||||
# Step 2: Build article HTML
|
||||
age_labels = {
|
||||
"2+": "Für die Kleinsten (ab 2–3 Jahre)",
|
||||
"3+": "Für die Kleinsten (ab 3 Jahre)",
|
||||
"4+": "Ab 4 Jahre",
|
||||
"5+": "Ab 5 Jahre",
|
||||
"6+": "Ab 6 Jahre",
|
||||
"7+": "Ab 7 Jahre",
|
||||
"Allgemein": "Weitere Kinderspiele",
|
||||
}
|
||||
|
||||
age_order = ["2+", "3+", "4+", "5+", "6+", "7+", "Allgemein"]
|
||||
|
||||
# Build deal blocks by age
|
||||
sections = []
|
||||
for age in age_order:
|
||||
if age not in age_groups:
|
||||
continue
|
||||
games = age_groups[age]
|
||||
label = age_labels.get(age, age)
|
||||
blocks = []
|
||||
for g in games:
|
||||
blocks.append(f"""<div style="display: flex; gap: 1rem; padding: 1rem 0; border-bottom: 1px solid #eee;">
|
||||
<img src="{g['img']}" alt="" style="width: 100px; height: 100px; object-fit: contain;" loading="lazy" />
|
||||
<div style="flex: 1;"><strong>{g['title']}</strong><br />
|
||||
<span style="color: #b12704; font-size: 1.2rem; font-weight: bold;">{g['price']:.2f} €</span>
|
||||
<span style="text-decoration: line-through; color: #999; margin-left: 0.5rem;">UVP {g['uvp']:.2f} €</span>
|
||||
<span style="background: #b12704; color: #fff; padding: 2px 6px; border-radius: 3px; margin-left: 0.5rem; font-size: 0.85rem;">-{g['pct']}%</span>
|
||||
<span style="color: #b12704; margin-left: 0.3rem;">({g['saved']:.2f} € gespart)</span><br />
|
||||
<a href="https://www.amazon.de/dp/{g['asin']}?tag=60pro05-21" target="_blank" rel="noopener" style="color: #007185;">Zu Amazon →</a></div>
|
||||
</div>""")
|
||||
sections.append(f"<h2>{label}</h2>\n" + "\n".join(blocks))
|
||||
|
||||
total_count = len(deals)
|
||||
group_count = len(age_groups)
|
||||
|
||||
# Summary stats
|
||||
summary_lines = []
|
||||
for age in age_order:
|
||||
if age in age_groups:
|
||||
summary_lines.append(f"{age_labels.get(age, age)}: {len(age_groups[age])} Spiele")
|
||||
|
||||
intro_html = f"""<p>Jeden Sonntag durchforsten wir Amazon nach den besten Kinderspiel-Angeboten – heute mit {total_count} reduzierten Titeln für kleine Spieler.</p>
|
||||
<p>Die Angebote sind nach Altersgruppen sortiert: von den ersten Brettspielen für die Kleinsten bis zu spannenden Familienspielen für Schulkinder. Alle Preise inklusive Mehrwertsteuer, Stand 21. Juni 2026.</p>
|
||||
<p><strong>Unser Tipp:</strong> Monsterjäger von Schmidt Spiele ist heute mit 54 % Rabatt der absolute Preisknüller – nur 11,31 € statt 24,49 €!</p>
|
||||
<!--more-->"""
|
||||
|
||||
# Build full HTML
|
||||
all_sections = "\n".join(sections)
|
||||
|
||||
body_html = intro_html + all_sections + f"""
|
||||
<p style="margin-top: 2rem; color: #888; font-size: 0.9rem;">Preise und Verfugbarkeit konnen sich schnell andern. Uber die Links (Partner-Tag 60pro05-21) kauft ihr zum gleichen Preis – Amazon zahlt eine kleine Provision.</p>
|
||||
<p style="font-size: 0.8rem; color: #999; text-align: center; margin-top: 1rem;">Brettspiel-News.de · (C) liegt beim jeweiligen Rechteinhaber</p>"""
|
||||
|
||||
# Clean up extended chars for Joomla
|
||||
body_html = body_html.replace("ö", "ö").replace("ä", "ä").replace("ü", "ü").replace("ß", "ß")
|
||||
|
||||
print(f"\nArticle body: {len(body_html)} chars, {total_count} games, {group_count} groups")
|
||||
|
||||
# Step 3: POST article
|
||||
print("\nCreating Joomla article...")
|
||||
article = {
|
||||
"title": "🧒 Sonntags-Kinderspiele: Die 20 besten Angebote für kleine Spieler",
|
||||
"introtext": body_html,
|
||||
"fulltext": body_html,
|
||||
"catid": 61,
|
||||
"state": 0,
|
||||
"access": 6,
|
||||
"created_by": 560,
|
||||
"language": "de",
|
||||
"featured": 1,
|
||||
"metadesc": f"20 reduzierte Kinderspiele bei Amazon: Monsterjäger -54%, Pupsparade -42%, Sagaland -43% – sortiert nach Altersgruppen. Alle Deals mit Direktlinks.",
|
||||
"metakey": "Kinderspiele, Kinder Brettspiele, Angebote, Amazon Deals, Monsterjäger, Pupsparade, Sagaland, Kinderspiel des Jahres",
|
||||
"metadata": {"author": "Daniel Krause", "robots": "index, follow"},
|
||||
"images": {
|
||||
"image_intro": "https://www.brettspiel-news.de/images/Spiele/2026/monsterjaeger_kinderspiele_2026-06-21.jpg#joomlaImage://local-images/Spiele/2026/monsterjaeger_kinderspiele_2026-06-21.jpg?width=&height="
|
||||
},
|
||||
}
|
||||
|
||||
result = api_post("/content/articles", article)
|
||||
article_id = result["data"]["id"]
|
||||
print(f"\n✅ Article created! ID: {article_id}")
|
||||
print(f"URL: https://www.brettspiel-news.de/index.php?option=com_content&view=article&id={article_id}")
|
||||
print(f"Article server: http://185.162.249.159:8890/?key=br3ttsp1el-n3ws-2026")
|
||||
|
||||
# Print summary
|
||||
print(f"\n=== KINDERSPIELE TOP 20 ===")
|
||||
print(f"Spiele: {total_count}")
|
||||
print(f"Altersgruppen: {group_count}")
|
||||
for line in summary_lines:
|
||||
print(f" {line}")
|
||||
Reference in New Issue
Block a user