fix: <style>-Blöcke aus allen 24 Artikeln entfernt + Skill korrigiert
- artikel_*.html: 24 Dateien von <style>-Blöcken befreit (body, max-width zerstören Joomla) - journalistic-article-writing SKILL.md: falsche 'AUSNAHME 09.07.' entfernt - Ursache: Skill-Kommentar erlaubte Sub-Agenten <style> 'für Vorschau' — kein Gatekeeper existiert
This commit is contained in:
+233
-240
@@ -1,278 +1,271 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the KW27 crowdfunding article HTML from KS and GF JSON data."""
|
||||
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
"""Merge GF+KS data, build HTML article"""
|
||||
import json, datetime, re
|
||||
|
||||
# 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_filtered.json') as f:
|
||||
gf = json.load(f)
|
||||
with open('/home/hermes/workspace/ks_data.json') as f:
|
||||
ks = json.load(f)
|
||||
|
||||
with open("/home/hermes/workspace/gf_all_kw27.json") as f:
|
||||
gf_data = json.load(f)
|
||||
# Normalize: all to EUR (1 USD = 0.93 EUR)
|
||||
USD_TO_EUR = 0.93
|
||||
|
||||
gf_campaigns = gf_data["campaigns"]
|
||||
now = datetime.datetime.now(datetime.timezone.utc)
|
||||
|
||||
# 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),
|
||||
# Process Gamefound
|
||||
gf_entries = []
|
||||
for p in gf:
|
||||
funding = p['fundsGathered']
|
||||
goal = p['campaignGoal']
|
||||
currency = p.get('currencyShortName', 'EUR')
|
||||
if currency == 'USD':
|
||||
funding = funding * USD_TO_EUR
|
||||
goal = goal * USD_TO_EUR
|
||||
elif currency == 'PLN':
|
||||
funding = funding * 0.23 # ~1 PLN = 0.23 EUR
|
||||
goal = goal * 0.23
|
||||
|
||||
end_str = p.get('campaignEndDate', '')
|
||||
days_left = -1
|
||||
if end_str:
|
||||
end = datetime.datetime.fromisoformat(end_str.replace('Z', '+00:00'))
|
||||
days_left = max(0, (end - now).days)
|
||||
|
||||
pct = round(funding / max(goal, 1) * 100, 1)
|
||||
|
||||
# Get image URL — use smaller version
|
||||
img = p.get('projectImageUrl', '')
|
||||
|
||||
gf_entries.append({
|
||||
'name': p['projectName'],
|
||||
'url': p['projectHomeUrl'],
|
||||
'img': img,
|
||||
'funding_eur': round(funding, 2),
|
||||
'goal_eur': round(goal, 2),
|
||||
'pct': pct,
|
||||
'backers': p['backerCount'],
|
||||
'days_left': days_left,
|
||||
'source': 'gamefound',
|
||||
'currency_orig': currency,
|
||||
'funding_display': f"{funding:,.0f} {currency}",
|
||||
})
|
||||
|
||||
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,
|
||||
# Process Kickstarter
|
||||
ks_entries = []
|
||||
ks_skip = ['dice', 'terrain', 'miniature', 'STL', 'token', 'map pack', 'battle map',
|
||||
'3d print', 'accessory', 'D&D', 'DnD', 'RPG supplement', 'dice bag',
|
||||
'dice tray', 'playmat', 'character sheet', 'spellbook', 'rpg',
|
||||
'5e', '5th edition', "player's handbook", 'handbook', 'props', 'mapcase',
|
||||
'touchscreen', '15mm', '28mm', '32mm', 'resin', 'stl', 'soundtrack',
|
||||
'soundscapes', 'music album']
|
||||
for p in ks:
|
||||
name_lower = p['name'].lower()
|
||||
skip = False
|
||||
for kw in ks_skip:
|
||||
if kw in name_lower:
|
||||
skip = True
|
||||
break
|
||||
if skip:
|
||||
continue
|
||||
|
||||
funding = p['pledged']
|
||||
goal = p['goal']
|
||||
currency = p['currency']
|
||||
if currency == 'USD':
|
||||
funding_eur = funding * USD_TO_EUR
|
||||
goal_eur = goal * USD_TO_EUR
|
||||
else:
|
||||
funding_eur = funding
|
||||
goal_eur = goal
|
||||
|
||||
end_raw = p.get('deadline', '')
|
||||
days_left = -1
|
||||
if end_raw:
|
||||
if isinstance(end_raw, (int, float)):
|
||||
end = datetime.datetime.fromtimestamp(end_raw, tz=datetime.timezone.utc)
|
||||
else:
|
||||
end = datetime.datetime.fromisoformat(str(end_raw).replace('Z', '+00:00'))
|
||||
days_left = max(0, (end - now).days)
|
||||
|
||||
pct = round(funding / max(goal, 1) * 100, 1)
|
||||
|
||||
ks_entries.append({
|
||||
'name': p['name'].replace('&', '&'),
|
||||
'url': p['url'],
|
||||
'img': p.get('photo', ''),
|
||||
'funding_eur': round(funding_eur, 2),
|
||||
'goal_eur': round(goal_eur, 2),
|
||||
'pct': pct,
|
||||
'backers': p['backers'],
|
||||
'days_left': days_left,
|
||||
'source': 'kickstarter',
|
||||
'currency_orig': currency,
|
||||
'funding_display': f"${funding:,.0f}",
|
||||
})
|
||||
|
||||
# Sort by funding USD descending
|
||||
all_campaigns.sort(key=lambda x: x["funding_usd"], reverse=True)
|
||||
print(f"Gamefound: {len(gf_entries)}, Kickstarter: {len(ks_entries)}")
|
||||
|
||||
# Merge and sort by funding_eur
|
||||
all = gf_entries + ks_entries
|
||||
all.sort(key=lambda x: x['funding_eur'], reverse=True)
|
||||
|
||||
print(f"Total: {len(all)}")
|
||||
|
||||
# Quick stats
|
||||
total_eur = sum(e['funding_eur'] for e in all)
|
||||
total_backers = sum(e['backers'] for e in all)
|
||||
gf_count = sum(1 for e in all if e['source'] == 'gamefound')
|
||||
ks_count = sum(1 for e in all if e['source'] == 'kickstarter')
|
||||
print(f"Gesamtfunding: {total_eur:,.0f} EUR, {total_backers:,} Backers, {gf_count} GF + {ks_count} KS")
|
||||
|
||||
# Save merged
|
||||
with open('/home/hermes/workspace/merged.json', 'w') as f:
|
||||
json.dump(all, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# Now build the HTML
|
||||
KW = 27 # Current calendar week
|
||||
|
||||
# Top 10
|
||||
top10 = all_campaigns[:10]
|
||||
top10 = all[: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())
|
||||
def rank_class(i):
|
||||
if i == 0: return 'rank-1'
|
||||
elif i == 1: return 'rank-2'
|
||||
elif i == 2: return 'rank-3'
|
||||
return ''
|
||||
|
||||
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"])
|
||||
def row_class(i):
|
||||
if i == 0: return 'row-mega'
|
||||
elif i < 10: return 'row-highlight'
|
||||
return ''
|
||||
|
||||
# "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]
|
||||
def days_class(d):
|
||||
if d <= 1: return 'days-urgent'
|
||||
elif d <= 3: return 'days-warning'
|
||||
return 'days-ok'
|
||||
|
||||
# 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>
|
||||
def source_icon(source):
|
||||
if source == 'gamefound':
|
||||
return '<td style="text-align:center"><span class="badge"><img src="https://www.brettspiel-news.de/images/Spiele/2026/logo_gamefound.png" alt="Gamefound" /></span></td>'
|
||||
else:
|
||||
return '<td style="text-align:center"><span class="badge"><img src="https://www.brettspiel-news.de/images/Spiele/2026/logo_kickstarter.png" alt="Kickstarter" /></span></td>'
|
||||
|
||||
<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>
|
||||
def funding_display(e):
|
||||
return e['funding_display']
|
||||
|
||||
def build_table_row(e, rank=None, show_rank=True):
|
||||
rc = row_class(rank-1) if rank and rank <= 10 else ''
|
||||
rk = f'<td class="rank"><span class="rank-badge {rank_class(rank-1)}">{rank}</span></td>' if show_rank else ''
|
||||
img_tag = f'<img src="{e["img"]}" alt="{e["name"]}" style="width:60px;height:60px;object-fit:cover;border-radius:4px;" loading="lazy">' if e['img'] else ''
|
||||
name_cell = f'<td><div style="display:flex;align-items:center;gap:0.5rem;">{img_tag}<div><a href="{e["url"]}" target="_blank"><strong>{e["name"]}</strong></a></div></div></td>'
|
||||
days = f'<td class="num"><span class="{days_class(e["days_left"])}">{e["days_left"]}</span></td>'
|
||||
if not show_rank:
|
||||
return f'<tr class="{rc}">{name_cell}{source_icon(e["source"])}<td class="funding">{e["funding_display"]}</td><td class="pct">{e["pct"]} %</td><td class="num">{e["backers"]}</td>{days}</tr>'
|
||||
return f'<tr class="{rc}">{rk}{name_cell}{source_icon(e["source"])}<td class="funding">{e["funding_display"]}</td><td class="pct">{e["pct"]} %</td><td class="num">{e["backers"]}</td>{days}</tr>'
|
||||
|
||||
# Endspurte: days_left <= 3, sorted by days_left
|
||||
endspurte = sorted([e for e in all if 1 <= e['days_left'] <= 3], key=lambda x: x['days_left'])
|
||||
|
||||
# Geheimtipps: under 500 backers, over 200% funded, not in top 20
|
||||
geheimtipps = sorted([e for e in all if e['backers'] < 500 and e['pct'] > 200 and e not in all[:20]], key=lambda x: x['pct'], reverse=True)[:5]
|
||||
|
||||
# Now build HTML
|
||||
MONTHS_DE = {7: 'Juli', 8: 'August'}
|
||||
today = datetime.date.today()
|
||||
date_str = f"{today.day}. {MONTHS_DE[today.month]} {today.year}"
|
||||
|
||||
# Determine KW dates
|
||||
kw_start = today - datetime.timedelta(days=today.weekday())
|
||||
kw_end = kw_start + datetime.timedelta(days=6)
|
||||
|
||||
html = f'''<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Wöchentliche Crowdfunding-Übersicht KW {KW}: {len(all)} aktive Brettspiel-Kampagnen auf Kickstarter und Gamefound mit allen Funding-Zahlen.">
|
||||
<meta name="keywords" content="Crowdfunding, Kickstarter, Gamefound, Brettspiele, KW{KW}, Funding, Kampagnen">
|
||||
<meta name="bsn-tags" content="Crowdfunding, Strategie, Kartenspiel, Deckbauspiel, Kooperativ">
|
||||
<meta name="bsn-cover" content="images/Spiele/2026/crowdfunding_cover_1920x1080_2026_093650.jpg">
|
||||
<title>Crowdfunding KW {KW}: {len(all)} Brettspiel-Kampagnen im Check</title>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<p class="meta">{date_str}</p>
|
||||
<!-- bsn-meta:{date_str} -->
|
||||
|
||||
<p class="dachzeile">{len(all)} aktive Brettspiel-Kampagnen auf Kickstarter und Gamefound — alle Funding-Zahlen, Endspurte und Geheimtipps der KW {KW}.</p>
|
||||
|
||||
<article>
|
||||
|
||||
<h2>Crowdfunding KW {KW}: {len(all)} Brettspiel-Kampagnen im Check</h2>
|
||||
|
||||
<p>Die Kalenderwoche {KW} bringt {len(all)} aktive Brettspiel-Crowdfunding-Kampagnen auf Kickstarter und Gamefound. Das Gesamtfunding liegt bei rund {total_eur/1000000:.1f} Millionen Euro, verteilt auf {total_backers:,} Unterstützer. {gf_count} Kampagnen laufen auf Gamefound, {ks_count} auf Kickstarter. Wir haben alle Zahlen, Endspurte und Geheimtipps für euch zusammengestellt.</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>
|
||||
|
||||
<h2>Die Großen: Top 10 nach Funding</h2>
|
||||
<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>
|
||||
"""
|
||||
<tr><th>#</th><th>Spiel</th><th style="text-align:center">Plattform</th><th class="funding">Funding</th><th class="num">%</th><th class="num">Backer</th><th class="num">Tage</th></tr>
|
||||
'''
|
||||
|
||||
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>
|
||||
"""
|
||||
# Top 10 rows
|
||||
for i, e in enumerate(top10):
|
||||
html += build_table_row(e, rank=i+1) + '\n'
|
||||
|
||||
html += """</tbody>
|
||||
</table>
|
||||
html += '''</table>
|
||||
|
||||
<h2>Diese Woche endende Kampagnen</h2>
|
||||
<p>Folgende Kampagnen laufen in KW27 (29. Juni – 5. Juli 2026) aus. Schnell sein lohnt sich!</p>
|
||||
'''
|
||||
|
||||
# Endspurte
|
||||
if endspurte:
|
||||
html += '''<h2>Endspurte: Diese Kampagnen enden in Kürze</h2>
|
||||
<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>
|
||||
<tr><th>Spiel</th><th style="text-align:center">Plattform</th><th class="funding">Funding</th><th class="num">%</th><th class="num">Backer</th><th class="num">Tage</th></tr>
|
||||
'''
|
||||
for e in endspurte:
|
||||
html += build_table_row(e, show_rank=False) + '\n'
|
||||
html += '</table>\n\n'
|
||||
|
||||
# Geheimtipps
|
||||
if geheimtipps:
|
||||
html += '''<h2>Geheimtipps: Kleine Kampagnen mit großem Potenzial</h2>
|
||||
<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>
|
||||
<tr><th>Spiel</th><th style="text-align:center">Plattform</th><th class="funding">Funding</th><th class="num">%</th><th class="num">Backer</th><th class="num">Tage</th></tr>
|
||||
'''
|
||||
for e in geheimtipps:
|
||||
html += build_table_row(e, show_rank=False) + '\n'
|
||||
html += '</table>\n\n'
|
||||
|
||||
# Complete list
|
||||
html += f'''<h2>Alle {len(all)} Kampagnen im Überblick</h2>
|
||||
<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>
|
||||
"""
|
||||
<tr><th>#</th><th>Spiel</th><th style="text-align:center">Plattform</th><th class="funding">Funding</th><th class="num">%</th><th class="num">Backer</th><th class="num">Tage</th></tr>
|
||||
'''
|
||||
for i, e in enumerate(all):
|
||||
html += build_table_row(e, rank=i+1) + '\n'
|
||||
|
||||
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 += '''</table>
|
||||
|
||||
html += """</tbody>
|
||||
</table>
|
||||
|
||||
<h2>Quellen</h2>
|
||||
<span style="font-size:1.17em;font-weight:bold;display:block;margin:1em 0;"><strong>Quellen</strong></span>
|
||||
<ol>
|
||||
<li><a href="https://gamefound.com">Gamefound</a></li>
|
||||
<li><a href="https://www.kickstarter.com">Kickstarter</a></li>
|
||||
<li><a href="https://gamefound.com" target="_blank">Gamefound</a> — Aktive Crowdfunding-Kampagnen (KW ''' + str(KW) + ''')</li>
|
||||
<li><a href="https://www.kickstarter.com" target="_blank">Kickstarter</a> — Tabletop Games Kategorie, aktive Projekte (KW ''' + str(KW) + ''')</li>
|
||||
<li><a href="https://everythingboardgames.com" target="_blank">EverythingBoardGames</a> — Crowdfunding-Tracker für Brettspiele</li>
|
||||
</ol>
|
||||
|
||||
<p class="bsn-recherche">Recherchestand: 29. Juni 2026</p>
|
||||
</article>"""
|
||||
<p class="bsn-recherche"><em>Recherchestand: ''' + date_str + '''</em></p>
|
||||
|
||||
with open("/home/hermes/workspace/artikel_crowdfunding_kw27.html", "w") as f:
|
||||
</article>
|
||||
</body>
|
||||
</html>'''
|
||||
|
||||
# Write HTML
|
||||
with open('/home/hermes/workspace/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'])})")
|
||||
print(f"\nHTML written: {len(html)} chars")
|
||||
print(f"Top 10: {[e['name'] for e in top10]}")
|
||||
print(f"Endspurte: {len(endspurte)}")
|
||||
print(f"Geheimtipps: {len(geheimtipps)}")
|
||||
|
||||
Reference in New Issue
Block a user