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
This commit is contained in:
Hermes Agent
2026-07-03 11:35:39 +02:00
parent 0bc68ecd25
commit b3b6b702d1
354 changed files with 180125 additions and 9391 deletions
+278
View File
@@ -0,0 +1,278 @@
#!/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'])})")