Feature: Tierschutz-Check, KI-Züchter-Features, Export, SEO-Update

Tierschutz-System (immer aktiv, nicht abschaltbar):
- welfare_check.py: regelbasierte Prüfung IK, Alter, Deckpause, Wurfanzahl, Genetik
- Grün/Gelb/Rot-Modal bei Wurf anlegen + Probeverpaarung
- Bei kritischem Befund + "Trotzdem fortfahren" → automatische Admin-Mail
- Tierschutz-Check nie durch Nutzer deaktivierbar

KI-Züchter-Features (pro User an/abschaltbar außer Tierschutz):
- routes/zucht_ki.py: 5 Endpunkte — Wurfankündigung, Genetik-Erklärung,
  Paarungsanalyse, Hund-Beschreibung, Jahresbericht
- Toggles in Einstellungen (ki_zucht_* Felder)
- KI-Buttons in litters.js + zuchthunde.js

KI-Routing: Privilegierte Rollen (Admin, Züchter, Moderator, Manager)
nutzen Claude Sonnet primär, lokales LLM als Fallback

Datenexport: routes/breeder_export.py — ZIP mit HTML-Dossier + ODS
(odfpy hinzugefügt in requirements.txt)

Admin-Profil: POST /admin/breeder/create-profile für Schnellprofil ohne
Antragsprozess; Admin-Rolle bleibt erhalten

Wurfformular: Dropdown aus Zuchtkartei für Vater/Mutter mit Auto-Fill;
litters.vater_id + mutter_id als FK auf zucht_hunde

Probeverpaarung: heart-fill Icon + Welfare-Block im Ergebnis

Landing Page: Züchter-Section + Feature-Gruppe, Meta-Tags, JSON-LD,
keywords, softwareVersion 2.1

SEO: llms.txt vollständig überarbeitet, robots.txt Züchter-Pfade,
sitemap.xml um Wurfbörse + Züchter-Profile erweitert

SW by-v474, APP_VER 451
This commit is contained in:
rene 2026-04-28 19:49:54 +02:00
parent 91340be5a3
commit c8ae514c01
20 changed files with 2129 additions and 200 deletions

View file

@ -82,6 +82,28 @@ def _track_usage(user_id: int | None, source: str) -> None:
logger.warning(f"KI-Tracking fehlgeschlagen: {exc}")
def _is_cloud_priority_user(user_id: int | None) -> bool:
"""Privilegierte Rollen (Admin, Moderator, Züchter, Manager) nutzen Cloud-KI primär."""
if not user_id or not ANTHROPIC_KEY:
return False
try:
from database import db
with db() as conn:
user = conn.execute(
"SELECT rolle, is_moderator, is_social_media FROM users WHERE id=?",
(user_id,)
).fetchone()
if not user:
return False
return bool(
user["rolle"] in ("admin", "breeder", "moderator")
or user["is_moderator"]
or user["is_social_media"]
)
except Exception:
return False
def _check_weekly_cloud_limit(user_id: int | None) -> None:
"""Wirft KIPremiumRequired wenn user_id das wöchentliche Cloud-Limit erreicht hat."""
if user_id is None or CLOUD_WEEKLY_LIMIT <= 0:
@ -92,9 +114,9 @@ def _check_weekly_cloud_limit(user_id: int | None) -> None:
user = conn.execute(
"SELECT rolle, is_moderator FROM users WHERE id=?", (user_id,)
).fetchone()
# Admins, Moderatoren und Media Manager haben kein Limit
# Admins, Moderatoren, Züchter und Media Manager haben kein Limit
if user and (
user["rolle"] in ("admin", "moderator", "media_manager")
user["rolle"] in ("admin", "breeder", "moderator", "media_manager")
or user["is_moderator"]
):
return
@ -137,8 +159,28 @@ async def complete(
if requires_premium and not user_is_premium:
raise KIPremiumRequired("Dieses Feature ist Teil von Ban Yaro Premium.")
# Immer lokal zuerst — Cloud ist Fallback wenn lokal nicht erreichbar
if KI_MODE in ("local", "cloud"):
# Privilegierte Rollen (Admin, Moderator, Züchter, Manager) → Cloud zuerst
if _is_cloud_priority_user(user_id):
try:
_check_weekly_cloud_limit(user_id)
text = await _cloud_complete(prompt, system, max_tokens, json_mode)
_track_usage(user_id, "cloud")
if return_model:
return (text, CLOUD_MODEL)
return (text, "cloud") if return_source else text
except KIPremiumRequired:
raise
except Exception as e:
logger.warning(f"Cloud-KI nicht erreichbar für privilegierten User, Fallback lokal: {e}")
# Fallback auf lokales Modell
text = await _local_complete(prompt, system, max_tokens, json_mode)
_track_usage(user_id, "local")
if return_model:
return (text, LOCAL_MODEL)
return (text, "local") if return_source else text
# Standard-User → lokal zuerst, Cloud als Fallback
try:
text = await _local_complete(prompt, system, max_tokens, json_mode)
_track_usage(user_id, "local")