Files
BSN-Chatsystem/build_article.py
T
Hermes Agent b3b6b702d1 chore: vollständiger Workspace-Snapshot 03.07.2026
- publish_gatekeeper.py (neues Gatekeeper-System)
- 4 Premium-Artikel: Veggie Match, Berlin 1960, Wild Tiled West, Scream Park
- Alle kumulierten Artikel, Scripts, Medien und Caches
2026-07-03 11:35:39 +02:00

279 lines
9.0 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
"""Build the KW27 crowdfunding article HTML from KS and GF JSON data."""
import json
from datetime import datetime, timezone
# Load data
with open("/home/hermes/workspace/ks_all_kw27_full.json") as f:
ks_data = json.load(f)
with open("/home/hermes/workspace/gf_all_kw27.json") as f:
gf_data = json.load(f)
gf_campaigns = gf_data["campaigns"]
# Currency conversion rates (approximate, late June 2026)
FX = {
"USD": 1.0,
"EUR": 1.08,
"GBP": 1.28,
"PLN": 0.25,
"AUD": 0.65,
"CAD": 0.73,
"CHF": 1.12,
"HKD": 0.128,
"MXN": 0.055,
"SGD": 0.74,
}
def fmt_usd(val):
"""Format USD value nicely."""
if val >= 1000000:
return f"${val/1000000:.2f} Mio."
elif val >= 1000:
return f"${val/1000:,.0f}k".replace(",", ".")
else:
return f"${val:,.0f}"
def fmt_eur(val):
if val >= 1000000:
return f"{val/1000000:.2f} Mio. €"
elif val >= 1000:
return f"{val/1000:,.0f}k €".replace(",", ".")
else:
return f"{val:,.0f}"
def fmt_native(amount, currency):
"""Format amount in native currency."""
if amount >= 1000000:
return f"{amount/1000000:.2f} Mio. {currency}"
elif amount >= 1000:
return f"{amount/1000:,.0f}k {currency}".replace(",", ".")
else:
return f"{amount:,.0f} {currency}"
# Build unified campaign list
all_campaigns = []
for c in ks_data:
pledged_usd = float(c["pledged_usd"])
all_campaigns.append({
"name": c["name"],
"url": c["url"],
"funding_usd": pledged_usd,
"funding_native": c["pledged_native"],
"currency": c["currency"],
"goal_native": c["goal_native"],
"backers": c["backers"],
"deadline_unix": c["deadline_unix"],
"percent_funded": c["percent_funded"],
"creator": c["creator"],
"platform": "Kickstarter",
"blurb": c.get("blurb", ""),
"staff_pick": c.get("staff_pick", False),
})
for c in gf_campaigns:
funding_native = c["funding"]
currency = c["currency"]
funding_usd = funding_native * FX.get(currency, 1.0)
goal = c["goal"] if c["goal"] is not None else 0
pct = c["percent"] if c["percent"] is not None else 0
# Parse end_date
end_date_str = c["end_date"]
try:
end_dt = datetime.fromisoformat(end_date_str.replace("Z", "+00:00"))
deadline_unix = int(end_dt.timestamp())
except:
deadline_unix = 0
all_campaigns.append({
"name": c["name"],
"url": c["url"],
"funding_usd": funding_usd,
"funding_native": funding_native,
"currency": currency,
"goal_native": goal,
"backers": c["backers"],
"deadline_unix": deadline_unix,
"percent_funded": pct,
"creator": c["creator"],
"platform": "Gamefound",
"blurb": "",
"staff_pick": False,
})
# Sort by funding USD descending
all_campaigns.sort(key=lambda x: x["funding_usd"], reverse=True)
# Top 10
top10 = all_campaigns[:10]
# This week ending: June 29 - July 5, 2026
kw27_start = int(datetime(2026, 6, 29, 0, 0, 0, tzinfo=timezone.utc).timestamp())
kw27_end = int(datetime(2026, 7, 5, 23, 59, 59, tzinfo=timezone.utc).timestamp())
ending_this_week = [c for c in all_campaigns if kw27_start <= c["deadline_unix"] <= kw27_end]
ending_this_week.sort(key=lambda x: x["deadline_unix"])
# "Kleine Verlage - große Überraschungen": small/indie creators with high funding
# Criteria: goal <= 5000, funding > 10000, not in top 10
small_surprises = [c for c in all_campaigns
if c["goal_native"] <= 5000 and c["funding_usd"] > 10000
and c not in top10]
small_surprises.sort(key=lambda x: x["funding_usd"], reverse=True)
small_surprises = small_surprises[:8]
# Build HTML
html = """<article>
<title>Crowdfunding-Übersicht KW27: 115 Brettspiel-Kampagnen im Check</title>
<p class="meta">29. Juni 2026</p>
<p class="dachzeile">Jede Woche: Alle aktiven Brettspiel-Kampagnen auf Kickstarter und Gamefound</p>
<p>Die 27. Kalenderwoche 2026 bringt eine geballte Ladung Crowdfunding: Insgesamt <strong>115 aktive Brettspiel-Kampagnen</strong> haben wir diese Woche identifiziert <strong>72 auf Kickstarter</strong> und <strong>43 auf Gamefound</strong>. Von millionenschweren Schwergewichten bis zu kleinen Geheimtipps ist alles dabei. Wir haben alle Kampagnen für euch gecheckt und nach Funding sortiert.</p>
<hr id="system-readmore" />
<h2>Top 10 nach Funding (KW27)</h2>
<p>Die zehn aktuell am höchsten finanzierten Brettspiel-Kampagnen gemischt über beide Plattformen:</p>
<table class="bsn-table">
<thead>
<tr><th>#</th><th>Kampagne</th><th>Plattform</th><th>Funding</th><th>Backers</th><th>Verlag</th></tr>
</thead>
<tbody>
"""
for i, c in enumerate(top10, 1):
platform_label = "GF" if c["platform"] == "Gamefound" else "KS"
funding_str = fmt_native(c["funding_native"], c["currency"])
html += f"""<tr>
<td>{i}</td>
<td><a href="{c['url']}" target="_blank">{c['name']}</a></td>
<td>{platform_label}</td>
<td>{funding_str}</td>
<td>{c['backers']:,}</td>
<td>{c['creator']}</td>
</tr>
"""
html += """</tbody>
</table>
<h2>Diese Woche endende Kampagnen</h2>
<p>Folgende Kampagnen laufen in KW27 (29. Juni 5. Juli 2026) aus. Schnell sein lohnt sich!</p>
<table class="bsn-table">
<thead>
<tr><th>Kampagne</th><th>Plattform</th><th>Funding</th><th>Endet</th><th>Verlag</th></tr>
</thead>
<tbody>
"""
for c in ending_this_week:
platform_label = "GF" if c["platform"] == "Gamefound" else "KS"
funding_str = fmt_native(c["funding_native"], c["currency"])
end_date = datetime.fromtimestamp(c["deadline_unix"], tz=timezone.utc)
end_str = end_date.strftime("%d.%m.%Y")
html += f"""<tr>
<td><a href="{c['url']}" target="_blank">{c['name']}</a></td>
<td>{platform_label}</td>
<td>{funding_str}</td>
<td>{end_str}</td>
<td>{c['creator']}</td>
</tr>
"""
html += """</tbody>
</table>
<h2>Kleine Verlage große Überraschungen</h2>
<p>Nicht nur die großen Namen liefern ab. Diese Kampagnen kleiner Verlage haben mit bescheidenen Zielen gestartet und wurden zu echten Überraschungserfolgen:</p>
<table class="bsn-table">
<thead>
<tr><th>Kampagne</th><th>Plattform</th><th>Funding</th><th>Ziel</th><th>%</th><th>Backers</th><th>Verlag</th></tr>
</thead>
<tbody>
"""
for c in small_surprises:
platform_label = "GF" if c["platform"] == "Gamefound" else "KS"
funding_str = fmt_native(c["funding_native"], c["currency"])
goal_str = fmt_native(c["goal_native"], c["currency"])
pct_str = f"{c['percent_funded']:,.0f}%" if c['percent_funded'] else ""
html += f"""<tr>
<td><a href="{c['url']}" target="_blank">{c['name']}</a></td>
<td>{platform_label}</td>
<td>{funding_str}</td>
<td>{goal_str}</td>
<td>{pct_str}</td>
<td>{c['backers']:,}</td>
<td>{c['creator']}</td>
</tr>
"""
html += """</tbody>
</table>
<h2>Alle 115 Kampagnen nach Funding sortiert</h2>
<p>Die vollständige Liste aller aktiven Brettspiel-Kampagnen der KW27 sortiert nach Funding (höchstes zuerst):</p>
<table class="bsn-table">
<thead>
<tr><th>#</th><th>Kampagne</th><th>Plattform</th><th>Funding</th><th>Ziel</th><th>%</th><th>Backers</th><th>Verlag</th></tr>
</thead>
<tbody>
"""
for i, c in enumerate(all_campaigns, 1):
platform_label = "GF" if c["platform"] == "Gamefound" else "KS"
funding_str = fmt_native(c["funding_native"], c["currency"])
goal_str = fmt_native(c["goal_native"], c["currency"]) if c["goal_native"] > 0 else ""
pct_str = f"{c['percent_funded']:,.0f}%" if c['percent_funded'] else ""
html += f"""<tr>
<td>{i}</td>
<td><a href="{c['url']}" target="_blank">{c['name']}</a></td>
<td>{platform_label}</td>
<td>{funding_str}</td>
<td>{goal_str}</td>
<td>{pct_str}</td>
<td>{c['backers']:,}</td>
<td>{c['creator']}</td>
</tr>
"""
html += """</tbody>
</table>
<h2>Quellen</h2>
<ol>
<li><a href="https://gamefound.com">Gamefound</a></li>
<li><a href="https://www.kickstarter.com">Kickstarter</a></li>
</ol>
<p class="bsn-recherche">Recherchestand: 29. Juni 2026</p>
</article>"""
with open("/home/hermes/workspace/artikel_crowdfunding_kw27.html", "w") as f:
f.write(html)
# Print stats
print(f"Total campaigns: {len(all_campaigns)}")
print(f" Kickstarter: {sum(1 for c in all_campaigns if c['platform'] == 'Kickstarter')}")
print(f" Gamefound: {sum(1 for c in all_campaigns if c['platform'] == 'Gamefound')}")
print(f"Top 10: {len(top10)}")
print(f"Ending this week: {len(ending_this_week)}")
print(f"Small surprises: {len(small_surprises)}")
print(f"\nTop 10 list:")
for i, c in enumerate(top10, 1):
print(f" {i}. {c['name']} ({c['platform']}) - {fmt_native(c['funding_native'], c['currency'])}")
print(f"\nEnding this week:")
for c in ending_this_week:
end_date = datetime.fromtimestamp(c["deadline_unix"], tz=timezone.utc)
print(f" {c['name']} ({c['platform']}) - ends {end_date.strftime('%d.%m.%Y')}")
print(f"\nSmall surprises:")
for c in small_surprises:
print(f" {c['name']} ({c['platform']}) - {fmt_native(c['funding_native'], c['currency'])} (goal: {fmt_native(c['goal_native'], c['currency'])})")