Web-Quellen: An/Aus-Schalter pro Quelle im UI + Einzel-Quellen-Sync
This commit is contained in:
@@ -29,7 +29,7 @@ import threading
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from urllib.parse import quote
|
||||
|
||||
from fastapi import Depends, Form, Request
|
||||
from fastapi import Depends, Form, HTTPException, Request
|
||||
from fastapi.responses import RedirectResponse
|
||||
from sqlalchemy import func, or_, select
|
||||
|
||||
@@ -192,12 +192,41 @@ class NeuheitenPlugin(BasePlugin):
|
||||
@self.router.post("/neuheiten/quellen-sync")
|
||||
def quellen_jetzt_synchronisieren(
|
||||
user: User = Depends(require_roles(Role.ADMIN.value, Role.REDAKTEUR.value)),
|
||||
quelle: str = Form(""),
|
||||
):
|
||||
zeilen = self._quellen_sync_ausfuehren()
|
||||
text = " · ".join(z.als_text() for z in zeilen)
|
||||
ziel = f"/neuheiten?meldung={quote(f'Web-Quellen-Sync abgeschlossen: {text}')}"
|
||||
"""Gesamt-Sync oder Einzel-Quellen-Sync (`quelle` = Quellname)."""
|
||||
if quelle:
|
||||
zeilen = self._quellen_sync_ausfuehren(nur_quellen={quelle})
|
||||
ziel = f"/neuheiten?meldung={quote('Web-Quelle „' + quelle + '“ synchronisiert: ' + ' · '.join(z.als_text() for z in zeilen))}"
|
||||
else:
|
||||
zeilen = self._quellen_sync_ausfuehren()
|
||||
text = " · ".join(z.als_text() for z in zeilen)
|
||||
ziel = f"/neuheiten?meldung={quote(f'Web-Quellen-Sync abgeschlossen: {text}')}"
|
||||
return RedirectResponse(ziel[:2000], status_code=303)
|
||||
|
||||
@self.router.post("/neuheiten/quellen/{quellen_name}/schalter")
|
||||
def quellen_schalter(
|
||||
quellen_name: str,
|
||||
user: User = Depends(require_roles(Role.ADMIN.value, Role.REDAKTEUR.value)),
|
||||
aktiv: str = Form(...),
|
||||
):
|
||||
"""An/Aus-Schalter einer Web-Quelle (übersteuert den Env-Standard)."""
|
||||
if quellen_name not in {k.name for k in ADAPTER_KLASSEN}:
|
||||
raise HTTPException(status_code=404, detail="Unbekannte Quelle")
|
||||
assert self.context is not None and self.context.session_factory is not None
|
||||
with self.context.session_factory() as db:
|
||||
eintrag = db.scalar(
|
||||
select(QuellenStatus).where(QuellenStatus.quelle == quellen_name)
|
||||
)
|
||||
if eintrag is None:
|
||||
eintrag = QuellenStatus(quelle=quellen_name, url="")
|
||||
db.add(eintrag)
|
||||
eintrag.aktiv_schalter = aktiv == "1"
|
||||
db.commit()
|
||||
zustand = "eingeschaltet" if aktiv == "1" else "ausgeschaltet"
|
||||
ziel = f"/neuheiten?meldung={quote(f'Quelle „{quellen_name}“ {zustand}.')}"
|
||||
return RedirectResponse(ziel, status_code=303)
|
||||
|
||||
def _quellen_uebersicht(self, db) -> list[dict]:
|
||||
"""Zeilen der Sync-Übersicht: Adapter + letzter Lauf pro Quelle."""
|
||||
status_nach_quelle = {
|
||||
@@ -207,12 +236,18 @@ class NeuheitenPlugin(BasePlugin):
|
||||
ansichten = []
|
||||
for klasse in ADAPTER_KLASSEN:
|
||||
status = status_nach_quelle.get(klasse.name)
|
||||
schalter = status.aktiv_schalter if status is not None else None
|
||||
aktiv = (
|
||||
schalter
|
||||
if schalter is not None
|
||||
else quelle_aktiv(klasse.name)
|
||||
)
|
||||
ansichten.append(
|
||||
{
|
||||
"name": klasse.name,
|
||||
"anzeigename": klasse.anzeigename,
|
||||
"url": klasse.start_urls[0] if klasse.start_urls else "",
|
||||
"aktiv": quelle_aktiv(klasse.name),
|
||||
"aktiv": aktiv,
|
||||
"letzte_laufzeit": status.letzte_laufzeit if status else None,
|
||||
"dauer_sekunden": status.dauer_sekunden if status else None,
|
||||
"neu": status.anzahl_neu if status else 0,
|
||||
@@ -301,12 +336,33 @@ class NeuheitenPlugin(BasePlugin):
|
||||
"ALTER TABLE neuheiten ADD COLUMN quellen_url VARCHAR(500)"
|
||||
)
|
||||
|
||||
def aktiv_schalter_spalte(conn) -> None:
|
||||
"""Migration 0006: UI-Schalter pro Web-Quelle (nullable Boolean)."""
|
||||
info = conn.exec_driver_sql(
|
||||
"PRAGMA table_info(neuheiten_quellen_status)"
|
||||
).fetchall()
|
||||
vorhanden = any(zeile[1] == "aktiv_schalter" for zeile in info)
|
||||
if conn.dialect.name == "sqlite" and vorhanden:
|
||||
return
|
||||
if conn.dialect.name != "sqlite":
|
||||
existiert = conn.exec_driver_sql(
|
||||
"SELECT column_name FROM information_schema.columns "
|
||||
"WHERE table_name = 'neuheiten_quellen_status' "
|
||||
"AND column_name = 'aktiv_schalter'"
|
||||
).scalar()
|
||||
if existiert is not None:
|
||||
return
|
||||
conn.exec_driver_sql(
|
||||
"ALTER TABLE neuheiten_quellen_status ADD COLUMN aktiv_schalter BOOLEAN"
|
||||
)
|
||||
|
||||
return [
|
||||
Migration(version="0001_neuheiten_tabelle", up=neuheiten_tabelle),
|
||||
Migration(version="0002_bgg_id_nullable", up=bgg_id_nullable),
|
||||
Migration(version="0003_quellen_status", up=quellen_status_tabelle),
|
||||
Migration(version="0004_bild_url", up=bild_url_spalte),
|
||||
Migration(version="0005_quellen_url", up=quellen_url_spalte),
|
||||
Migration(version="0006_aktiv_schalter", up=aktiv_schalter_spalte),
|
||||
]
|
||||
|
||||
def navigation(self) -> list[NavEntry]:
|
||||
@@ -386,8 +442,14 @@ class NeuheitenPlugin(BasePlugin):
|
||||
finally:
|
||||
self._sync_sperre.release()
|
||||
|
||||
def _quellen_sync_ausfuehren(self) -> list[QuellenLaufZeile]:
|
||||
"""Ein Lauf über alle aktiven Web-Quellen (Fehler pro Quelle isoliert)."""
|
||||
def _quellen_sync_ausfuehren(
|
||||
self, nur_quellen: set[str] | None = None
|
||||
) -> list[QuellenLaufZeile]:
|
||||
"""Ein Lauf über alle aktiven Web-Quellen (Fehler pro Quelle isoliert).
|
||||
|
||||
`nur_quellen`: optional nur diese Quellnamen (manueller Einzellauf,
|
||||
ignoriert den An/Aus-Schalter der Quelle).
|
||||
"""
|
||||
registry = (
|
||||
self.context.registry
|
||||
if self.context is not None and self.context.registry is not None
|
||||
@@ -398,7 +460,7 @@ class NeuheitenPlugin(BasePlugin):
|
||||
service = WebQuellenSyncService(
|
||||
self.context.session_factory, dedup=dedup, audit=audit
|
||||
)
|
||||
return service.laufe()
|
||||
return service.laufe(nur_quellen=nur_quellen)
|
||||
|
||||
def _quellen_sync_job(self) -> None:
|
||||
"""Hintergrund-Job: holt regelmäßig die Web-Quellen ab."""
|
||||
|
||||
Reference in New Issue
Block a user