Compare commits

..

No commits in common. "c3d33547c72192f6fddf7dfc5453d7a60c340f89" and "0af3078a2a55d6c713d78e2eea5439ed0f128190" have entirely different histories.

6 changed files with 30 additions and 59 deletions

View file

@ -671,49 +671,23 @@ async def wiki_evaluate(sample: int = 20, user=Depends(require_mod)):
# ------------------------------------------------------------------
# GET /api/admin/wiki/enrichment-status — Enrichment-Statistik
# POST /api/admin/wiki/reset-gemma — Gemma-Einträge zurücksetzen
# ------------------------------------------------------------------
@router.get("/wiki/enrichment-status")
async def wiki_enrichment_status(user=Depends(require_mod)):
with db() as conn:
total = conn.execute("SELECT COUNT(*) FROM wiki_rassen").fetchone()[0]
enriched = conn.execute("SELECT COUNT(*) FROM wiki_rassen WHERE ki_enriched=1").fetchone()[0]
no_wiki = conn.execute("SELECT COUNT(*) FROM wiki_rassen WHERE ki_enriched=2").fetchone()[0]
pending = conn.execute("SELECT COUNT(*) FROM wiki_rassen WHERE ki_enriched=0").fetchone()[0]
by_model = {
row[0] or "unbekannt": row[1]
for row in conn.execute(
"SELECT ki_model, COUNT(*) FROM wiki_rassen "
"WHERE ki_enriched=1 GROUP BY ki_model ORDER BY 2 DESC"
).fetchall()
}
with_photo = conn.execute(
"SELECT COUNT(*) FROM wiki_rassen WHERE foto_url IS NOT NULL AND foto_url != ''"
).fetchone()[0]
return {
"total": total,
"enriched": enriched,
"no_wiki": no_wiki,
"pending": pending,
"with_photo": with_photo,
"by_model": by_model,
}
@router.post("/wiki/reset-gemma")
async def wiki_reset_gemma(user=Depends(require_mod)):
from scraper.breed_enricher import reset_gemma_entries
count = reset_gemma_entries()
return {"reset": count}
# ------------------------------------------------------------------
# POST /api/admin/wiki/fetch-photos — Wiki-Fotos laden
# POST /api/admin/wiki/translate-temperament — einmalige Migration
# ------------------------------------------------------------------
@router.post("/wiki/fetch-photos")
async def wiki_fetch_photos(limit: int = 50, user=Depends(require_mod)):
import asyncio, subprocess
proc = await asyncio.create_subprocess_exec(
"python3", "/app/scraper/fetch_wiki_images.py", "--limit", str(limit),
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
)
stdout, stderr = await proc.communicate()
lines = (stdout + stderr).decode()
found = lines.count("Foto gespeichert")
return {"launched": True, "found": found, "log": lines[-2000:]}
@router.post("/wiki/translate-temperament")
async def wiki_translate_temperament(user=Depends(require_mod)):
from scraper.breed_enricher import translate_existing_temperaments
updated = translate_existing_temperaments()
return {"updated": updated}
# ------------------------------------------------------------------

View file

@ -4029,10 +4029,9 @@ html.modal-open {
white-space: nowrap;
}
.wiki-badge-groesse--klein { background: #e0f2fe; color: #0369a1; }
.wiki-badge-groesse--mittel { background: #fef9c3; color: #854d0e; }
.wiki-badge-groesse--gross { background: #fce7f3; color: #9d174d; }
.wiki-badge-groesse--sehr_gross { background: #f3e8ff; color: #6b21a8; }
.wiki-badge-groesse--klein { background: #e0f2fe; color: #0369a1; }
.wiki-badge-groesse--mittel { background: #fef9c3; color: #854d0e; }
.wiki-badge-groesse--gross { background: #fce7f3; color: #9d174d; }
.wiki-badge-aktivitaet--niedrig { background: #dcfce7; color: #166534; }
.wiki-badge-aktivitaet--mittel { background: #fef3c7; color: #92400e; }

View file

@ -187,7 +187,7 @@
<div class="sidebar-item" data-page="moderation" id="sidebar-moderation"
style="display:none;color:var(--c-warning,#f59e0b)">
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#eye"></use></svg> Moderation
<svg class="ph-icon" aria-hidden="true"><use href="/icons/phosphor.svg#shield"></use></svg> Moderation
</div>
<div class="sidebar-item" data-page="admin" id="sidebar-admin"

View file

@ -3,7 +3,7 @@
Router, State-Management, Navigation, Initialisierung.
============================================================ */
const APP_VER = '346'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const APP_VER = '345'; // ← bei jedem Deploy mit Frontend-Änderungen erhöhen
const App = (() => {

View file

@ -739,14 +739,14 @@ window.Page_admin = (() => {
<div style="font-size:var(--text-sm);font-weight:var(--weight-semibold);
color:var(--c-text);margin-bottom:var(--space-3)">Wartung</div>
<div style="display:flex;flex-wrap:wrap;gap:var(--space-2)">
<button class="btn btn-secondary btn-sm" id="adm-enrichment-status">
${UI.icon('arrows-clockwise')} Enrichment-Status
<button class="btn btn-secondary btn-sm" id="adm-translate-temper">
${UI.icon('translate')} Temperament Deutsch
</button>
<button class="btn btn-secondary btn-sm" id="adm-evaluate-breeds">
${UI.icon('chart-bar')} Qualitätsbewertung (20 Rassen)
</button>
<button class="btn btn-secondary btn-sm" id="adm-fetch-photos">
${UI.icon('image')} Fotos laden
<button class="btn btn-danger btn-sm" id="adm-reset-gemma">
${UI.icon('arrow-counter-clockwise')} Gemma-Einträge zurücksetzen
</button>
</div>
<div id="adm-maint-result" style="margin-top:var(--space-2);font-size:var(--text-xs);
@ -792,16 +792,14 @@ window.Page_admin = (() => {
});
el.querySelector('#adm-log-refresh').addEventListener('click', loadLogs);
el.querySelector('#adm-log-level').addEventListener('change', loadLogs);
el.querySelector('#adm-enrichment-status').addEventListener('click', async (e) => {
el.querySelector('#adm-translate-temper').addEventListener('click', async (e) => {
const btn = e.currentTarget;
const res = el.querySelector('#adm-maint-result');
btn.disabled = true;
res.textContent = 'Lade…';
res.textContent = 'Läuft…';
try {
const d = await API.get('/admin/wiki/enrichment-status');
const modelList = Object.entries(d.by_model)
.map(([m, n]) => `${m}: ${n}`).join(', ');
res.textContent = `Gesamt: ${d.total} | Angereichert: ${d.enriched} | Kein Wiki: ${d.no_wiki} | Ausstehend: ${d.pending} | Mit Foto: ${d.with_photo} | Modelle: ${modelList || ''}`;
const d = await API.post('/admin/wiki/translate-temperament', {});
res.textContent = `${d.updated} Rassen übersetzt`;
} catch (err) {
res.textContent = '✗ Fehler: ' + (err.message || err);
} finally {
@ -809,14 +807,14 @@ window.Page_admin = (() => {
}
});
el.querySelector('#adm-fetch-photos').addEventListener('click', async (e) => {
el.querySelector('#adm-reset-gemma').addEventListener('click', async (e) => {
if (!confirm('Alle Gemma-angereicherten Einträge zurücksetzen? Sie werden beim nächsten Job neu (Wikipedia-grounded) angereichert.')) return;
const btn = e.currentTarget;
const res = el.querySelector('#adm-maint-result');
btn.disabled = true;
res.textContent = 'Fotos werden geladen… (kann 3060s dauern)';
try {
const d = await API.post('/admin/wiki/fetch-photos?limit=50', {});
res.textContent = `${d.found} Foto(s) gespeichert`;
const d = await API.post('/admin/wiki/reset-gemma', {});
res.textContent = `${d.reset} Gemma-Einträge zurückgesetzt`;
} catch (err) {
res.textContent = '✗ Fehler: ' + (err.message || err);
} finally {

View file

@ -3,7 +3,7 @@
Offline-Cache + Push Notifications + Tile-Cache
============================================================ */
const CACHE_VERSION = 'by-v361';
const CACHE_VERSION = 'by-v360';
const CACHE_STATIC = `${CACHE_VERSION}-static`;
const CACHE_TILES = 'ban-yaro-tiles-v1'; // bleibt über SW-Updates erhalten