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:
parent
91340be5a3
commit
c8ae514c01
20 changed files with 2129 additions and 200 deletions
|
|
@ -29,6 +29,8 @@ def _require_breeder(user=Depends(get_current_user)):
|
|||
class LitterCreate(BaseModel):
|
||||
vater_name: Optional[str] = None
|
||||
mutter_name: Optional[str] = None
|
||||
vater_id: Optional[int] = None # FK zucht_hunde
|
||||
mutter_id: Optional[int] = None # FK zucht_hunde
|
||||
geburt_datum: Optional[str] = None # YYYY-MM-DD
|
||||
erwartetes_datum: Optional[str] = None # YYYY-MM-DD
|
||||
welpen_gesamt: Optional[int] = None
|
||||
|
|
@ -44,6 +46,8 @@ class LitterCreate(BaseModel):
|
|||
class LitterUpdate(BaseModel):
|
||||
vater_name: Optional[str] = None
|
||||
mutter_name: Optional[str] = None
|
||||
vater_id: Optional[int] = None
|
||||
mutter_id: Optional[int] = None
|
||||
geburt_datum: Optional[str] = None
|
||||
erwartetes_datum: Optional[str] = None
|
||||
welpen_gesamt: Optional[int] = None
|
||||
|
|
@ -185,14 +189,17 @@ async def create_litter(body: LitterCreate, user=Depends(_require_breeder)):
|
|||
|
||||
cur = conn.execute(
|
||||
"""INSERT INTO litters
|
||||
(breeder_id, vater_name, mutter_name, geburt_datum, erwartetes_datum,
|
||||
(breeder_id, vater_name, mutter_name, vater_id, mutter_id,
|
||||
geburt_datum, erwartetes_datum,
|
||||
welpen_gesamt, welpen_verfuegbar, beschreibung, gesundheitstests,
|
||||
preis_spanne, status, sichtbar, sichtbar_bis)
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||||
(
|
||||
profile["id"],
|
||||
body.vater_name,
|
||||
body.mutter_name,
|
||||
body.vater_id,
|
||||
body.mutter_id,
|
||||
body.geburt_datum,
|
||||
body.erwartetes_datum,
|
||||
body.welpen_gesamt,
|
||||
|
|
@ -205,10 +212,78 @@ async def create_litter(body: LitterCreate, user=Depends(_require_breeder)):
|
|||
body.sichtbar_bis,
|
||||
)
|
||||
)
|
||||
litter_id = cur.lastrowid
|
||||
row = conn.execute(
|
||||
"SELECT * FROM litters WHERE id=?", (cur.lastrowid,)
|
||||
"SELECT * FROM litters WHERE id=?", (litter_id,)
|
||||
).fetchone()
|
||||
return dict(row)
|
||||
|
||||
# Tierschutz-Check
|
||||
from welfare_check import check_welfare
|
||||
welfare = check_welfare(
|
||||
conn, profile["id"],
|
||||
vater_id=body.vater_id,
|
||||
mutter_id=body.mutter_id,
|
||||
)
|
||||
# Welfare-Level speichern
|
||||
conn.execute(
|
||||
"UPDATE litters SET welfare_level=? WHERE id=?",
|
||||
(welfare["level"], litter_id)
|
||||
)
|
||||
|
||||
result = dict(row)
|
||||
result["welfare"] = welfare
|
||||
return result
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# POST /api/litters/{id}/welfare-confirm — Tierschutz-Hinweis bestätigt
|
||||
# ------------------------------------------------------------------
|
||||
@router.post("/litters/{litter_id}/welfare-confirm")
|
||||
async def welfare_confirm(litter_id: int, user=Depends(_require_breeder)):
|
||||
from mailer import send_email
|
||||
import os, logging as _log
|
||||
_logger = _log.getLogger(__name__)
|
||||
|
||||
with db() as conn:
|
||||
litter = _check_litter_owner(litter_id, user, conn)
|
||||
conn.execute(
|
||||
"UPDATE litters SET welfare_acknowledged=1 WHERE id=?", (litter_id,)
|
||||
)
|
||||
welfare_level = litter.get("welfare_level", "")
|
||||
|
||||
if welfare_level == "critical":
|
||||
# Admin benachrichtigen
|
||||
profile = conn.execute(
|
||||
"SELECT bp.zwingername, u.name, u.email "
|
||||
"FROM breeder_profiles bp JOIN users u ON u.id=bp.user_id "
|
||||
"WHERE bp.user_id=?", (user["id"],)
|
||||
).fetchone()
|
||||
admin_email = os.getenv("ADMIN_EMAIL", "mail@motocamp.de")
|
||||
app_url = os.getenv("APP_URL", "https://banyaro.app")
|
||||
zuechter = profile["name"] if profile else user.get("name", "Unbekannt")
|
||||
zwinger = profile["zwingername"] if profile else "—"
|
||||
eltern = conn.execute(
|
||||
"SELECT vater_name, mutter_name FROM litters WHERE id=?", (litter_id,)
|
||||
).fetchone()
|
||||
html = f"""
|
||||
<h2>Tierschutz-Hinweis bestätigt</h2>
|
||||
<p>Züchter <b>{zuechter}</b> (Zwinger: {zwinger}) hat einen Wurf mit
|
||||
kritischen Tierschutz-Hinweisen trotzdem angelegt.</p>
|
||||
<p>Vater: {eltern['vater_name'] or '—'} · Mutter: {eltern['mutter_name'] or '—'}</p>
|
||||
<p>Wurf-ID: {litter_id}</p>
|
||||
<p><a href="{app_url}/admin">Im Admin-Bereich prüfen</a></p>
|
||||
"""
|
||||
try:
|
||||
await send_email(
|
||||
admin_email,
|
||||
f"[Banyaro Tierschutz] Kritischer Hinweis bestätigt — {zwinger}",
|
||||
html,
|
||||
f"Züchter {zuechter} hat Wurf #{litter_id} trotz kritischer Tierschutz-Hinweise angelegt.",
|
||||
)
|
||||
except Exception as e:
|
||||
_logger.warning(f"Tierschutz-Admin-Mail fehlgeschlagen: {e}")
|
||||
|
||||
return {"message": "Bestätigt."}
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue