- Datenmodell + eigene Migration 0001_neuheiten_tabelle (Tabelle neuheiten: Titel, Verlag, Autor, Erscheinungsjahr, BGG-ID, Status 'neuheit', Quelle, Zeitstempel; bgg_id eindeutig als Merge-Kriterium) - BoardGameGeek XML API2-Client (search + thing, Batches à 20 IDs): Rate-Limit >= 1 s zwischen Requests, Retry mit exponentiellem Backoff bei 5xx/429/Netzwerkfehlern, HTTP 202 gemäß Retry-After, robustes XML-Parsing; Transport/Uhr/Sleep injizierbar (keine echten Calls in Tests) - Filter: Erweiterungen (boardgameexpansion) auf Request- und Elementebene ausgeschlossen; Prototypen per Titel-Heuristik (BGG hat keinen Marker) - Sync-Service mit Update-statt-Duplikat-Logik über die eindeutige BGG-ID (Status bleibt erhalten); Fehler je Suchbegriff brechen den Lauf nicht ab - APScheduler-Hintergrundjob (Standard 24 h) mit Überlappungsschutz, abschaltbar/intervallkonfigurierbar per Env; manueller 'Jetzt synchronisieren'-Endpunkt nur für Admin/Redakteur, optional mit Sofort-Suchbegriff - UI /neuheiten: sortier-/filterbare Tabelle mit Volltextsuche (HTMX-Teilladung, noscript-fähig), deutsche Oberfläche, BGG-Links, Ergebnis-Banner - Plugin-Loader: idempotentes Laden (Modul-Caching), damit mehrere create_app()-Aufrufe dieselben Plugin-Klassen/Tabellen nutzen - Tests: Parsing, Erweiterungs-/Prototyp-Filter, Rate-Limit/Backoff/202, Update-statt-Duplikat, Rollen am Sync-Endpunkt, Scheduler-Lifecycle — ausschließlich mit gemockten BGG-Antworten (uv run pytest: 96 grün) - README/AGENTS: Plugin-Doku, Env-Variablen, Fortschrittstabelle aktualisiert
43 lines
1.3 KiB
Python
43 lines
1.3 KiB
Python
"""Zugriff auf die neuheiten-Plugin-Module über den vom Plugin-Loader
|
|
verwendeten Modulnamen (`spiele_redaktion_plugins.neuheiten`).
|
|
|
|
Der Kern teilt sich eine SQLAlchemy-MetaData; deshalb darf es pro Prozess
|
|
nur eine Modul-Identität geben. Tests importieren das Plugin daher über
|
|
diesen Helper statt direkt über `plugins.neuheiten`.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
from redaktionskern.plugin_loader import discover_plugins
|
|
|
|
_PROJEKT_WURZEL = Path(__file__).resolve().parents[1]
|
|
|
|
if "spiele_redaktion_plugins.neuheiten" not in sys.modules:
|
|
discover_plugins(
|
|
_PROJEKT_WURZEL / "plugins",
|
|
entry_point_group="spiele_redaktion.tests.keine_entry_points",
|
|
)
|
|
|
|
_neuheiten = sys.modules["spiele_redaktion_plugins.neuheiten"]
|
|
|
|
bgg = _neuheiten.bgg
|
|
sync_modul = _neuheiten.sync
|
|
models = _neuheiten.models
|
|
|
|
BggClient = bgg.BggClient
|
|
BggFehler = bgg.BggFehler
|
|
SuchTreffer = bgg.SuchTreffer
|
|
BggSpiel = bgg.BggSpiel
|
|
parse_suche = bgg.parse_suche
|
|
parse_things = bgg.parse_things
|
|
|
|
SyncService = sync_modul.SyncService
|
|
SyncErgebnis = sync_modul.SyncErgebnis
|
|
ist_moeglicher_prototyp = sync_modul.ist_moeglicher_prototyp
|
|
speichere_oder_aktualisiere = sync_modul.speichere_oder_aktualisiere
|
|
|
|
Neuheit = models.Neuheit
|
|
STATUS_NEUHEIT = models.STATUS_NEUHEIT
|