Files
Flo Hartmann 96d6af8163 Coverbilder (bild_url) + responsives Layout für Mobil/Tablet
Teil 1 — Coverbilder:
- Migration 0004_bild_url (neuheiten), 0002_bild_url (planung, archiv
  mit beiden Spiegel-Tabellen); nullable VARCHAR(500), SQLite↔Postgres
  portabel, bestehende Zeilen bleiben erhalten
- BGG-Client extrahiert Cover aus der Thing-Antwort (bevorzugt <image>,
  sonst <thumbnail>; protokoll-relative URLs → https)
- Sync-Upsert pflegt bild_url beim Update; fehlendes Bild löscht keines
- Web-Quellen übernehmen img-URLs aus den Listenelementen (src/data-src,
  relative Pfade gegen die Seiten-URL aufgelöst); SPIEL-Essen nutzt das
  BILDER-Feld des JSON-Endpunkts
- Verschiebung Neuheiten→Planung und Archivierung/Wiederherstellen
  schreiben bild_url mit
- Anzeige: kleine Vorschaubilder (40x56) in den Listen, groß (max 200px)
  in Planung-Bearbeiten/Prüf-Dialog; Platzhalter-Div ohne Bild;
  Hotlinking mit loading=lazy + decoding=async

Teil 2 — Responsives Layout:
- Basislayout: Burger-Menü (Alpine.js) auf Mobil, Desktop-Leiste ab md:
- Listen kollabieren <768px zu Karten-Ansichten (Cover links, Titel +
  Metadaten rechts, Status-Badge oben); Desktop behält Tabellenform
- Touch-Ziele >= 44px auf Mobil, Formulare vollbreit mit Labels oben
- Sync-Übersicht/Audit-Log bleiben horizontal scrollbare Tabellen

Tests: 15 neue Fälle (tests/test_covers.py) — Migration inkl.
Daten-Erhalt, BGG-Parsing gemockt, Quellen-Übernahme, Template-Render;
316 Tests grün. README aktualisiert.
2026-08-24 16:54:56 +00:00

67 lines
2.5 KiB
Python
Raw Permalink 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.
"""Adapter für die Brettspielbox-Messevorschau (A-Z-Liste).
Seite: …/brettspiel-neuheiten-spiel-2026-herbst-2026-a-z/ (WordPress).
Struktur (Stand Analyse August 2026): Die komplette A-Z-Liste steht auf
einer Seite in `div.wp-block-columns`-Blöcken — pro Buchstabe ein
Absatz (`p`) mit dem Buchstaben bzw. „0-9“ und eine `ul` mit einem
`li` pro Spiel im Format „Titel (Verlag)“; optionale Links im Eintrag
zeigen auf Vorstellungs-/Rezensionsartikel und dienen als quellen_url,
sonst gilt die Listenseite selbst.
"""
from __future__ import annotations
import re
from bs4 import BeautifulSoup
from .basis import QuellenAdapter, QuellenTreffer, bild_aus_element
TITEL_VERLAG_MUSTER = re.compile(r"^\s*(.+?)\s*\(([^()]*)\)\s*$")
class BrettspielboxQuelle(QuellenAdapter):
name = "brettspielbox"
anzeigename = "Brettspielbox Messevorschau (AZ)"
start_urls = (
"https://brettspielbox.de/brettspiel-neuheiten-spiel-2026-herbst-2026-a-z/",
)
def seite_verarbeiten(self, inhalt: bytes, url: str):
suppe = BeautifulSoup(inhalt, "html.parser")
treffer: list[QuellenTreffer] = []
for spalte in suppe.select(".wp-block-columns"):
for liste in spalte.find_all("ul"):
for eintrag in liste.find_all("li"):
text = eintrag.get_text(" ", strip=True)
titel, verlag = _zerlege_eintrag(text)
if not titel:
continue
link = eintrag.find("a", href=True)
from urllib.parse import urljoin
quellen_url = (
urljoin(url, link["href"]) if link is not None else url
)
treffer.append(
QuellenTreffer(
titel=titel,
verlag=verlag,
autor=None,
erscheinungsdatum_oder_quartal=None,
quellen_url=quellen_url,
bild_url=bild_aus_element(eintrag, url),
)
)
return treffer, []
def _zerlege_eintrag(text: str) -> tuple[str | None, str | None]:
"""„Titel (Verlag)“ → (Titel, Verlag); ohne Klammer nur Titel."""
fund = TITEL_VERLAG_MUSTER.match(text)
if fund is None:
return (text.strip() or None), None
verlag = fund.group(2).strip() or None
titel = fund.group(1).strip()
return (titel or None), verlag