Backend: - DB: 3 neue Indizes (forum_posts thread+user, routes user) — Forum/Routen-Queries - Caching: cache.py (TTL-Cache ohne neue Dependency) für 5 statische Listen (training_exercises, pflege_tipps, wiki_stats, wiki_gruppen, help_articles) - diary.py + breeder_photos.py: Bildverarbeitung (ffmpeg/PIL/EXIF) per run_in_executor → blockiert Event-Loop nicht mehr - scheduler.py: 11 kollidierende Jobs auf 5-Min-Intervalle gestaggert, coalesce=True - social.py: ORDER BY RANDOM() ohne LIMIT in 2 Stellen gefixt - alerts.py: Haversine-Loop bekommt SQL-Bounding-Box-Vorfilter Frontend: - sw.js: Tile-Cache mit LRU-Eviction (max 500 Einträge) - admin.js: Event-Listener-Leak — Tab-Klicks per Delegation statt N Listener - api.js: compressImage() Helper — Client-seitiges Resize auf max 2000px (HEIC/Videos/<500KB unverändert), integriert in 8 Upload-Stellen (diary, dog-profile×2, walks, poison, lost, health×2) Bump APP_VER 1071 → 1072 (sw.js, app.js, main.py, index.html)
1688 lines
118 KiB
Python
1688 lines
118 KiB
Python
"""
|
||
BAN YARO — Social Media Manager
|
||
KI-gestützte Content-Erstellung für TikTok & Instagram.
|
||
"""
|
||
|
||
import json
|
||
import logging
|
||
import random
|
||
from typing import Optional
|
||
|
||
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
|
||
from pydantic import BaseModel
|
||
|
||
from auth import get_current_user, require_social_media
|
||
from database import db
|
||
|
||
# ------------------------------------------------------------------
|
||
# Übungs-Bibliothek (gespiegelt aus uebungen.js)
|
||
# ------------------------------------------------------------------
|
||
_UEBUNGEN = [
|
||
# ── GRUNDKOMMANDOS ──────────────────────────────────────────────
|
||
{"id": "gk_sitz", "name": "Sitz", "kat": "Grundkommando", "schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Das erste Kommando — Basis für alles weitere.",
|
||
"schritte": ["Leckerli vor die Nase, langsam nach oben/hinten führen", "Hinterteil senkt sich → Markerwort + Leckerli", "Wort 'Sitz' erst nach 10 sicheren Wiederholungen einführen"],
|
||
"tipp": "Nie zu früh das Kommandowort einführen."},
|
||
{"id": "gk_platz", "name": "Platz", "kat": "Grundkommando", "schwierigkeit": "Anfänger", "alter": "Ab 10 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund legt sich auf Signal hin — wichtig für Ruhephasen und Wartesituationen.",
|
||
"schritte": ["Hund ins Sitz", "Leckerli senkrecht zwischen Vorderpfoten führen", "Ellenbogen + Hinterteil am Boden → belohnen", "Wort nach 10–15 Wiederholungen"],
|
||
"tipp": "Leckerli unter dein angewinkeltes Knie halten — Hund kriecht drunter durch."},
|
||
{"id": "gk_bleib", "name": "Bleib", "kat": "Grundkommando", "schwierigkeit": "Anfänger–Fortg.", "alter": "Ab 12 Wo", "dauer": "5 Min",
|
||
"beschreibung": "Drei Dimensionen: Dauer, Distanz, Ablenkung — immer nur eine steigern.",
|
||
"schritte": ["Sitz/Platz", "2 Sek warten → Markerwort + Leckerli", "Freigabewort 'Okay' einführen", "Schrittweise: 2 → 5 → 10 → 30 Sek", "Erst dann einen Schritt zurück"],
|
||
"tipp": "Immer zum Hund zurückkehren — nie ihn zu dir rufen beim Bleib."},
|
||
{"id": "gk_hier", "name": "Hier / Komm", "kat": "Grundkommando", "schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "5 Min",
|
||
"beschreibung": "Lebensrettend wichtig — zuverlässiger Rückruf in jeder Situation.",
|
||
"schritte": ["Wohnung, 2–3 Meter, hinknien", "Freudige Stimme: 'Hier!'", "Ankommen = Mega-Belohnung", "Nur einmal rufen", "Schleppleine im Garten"],
|
||
"tipp": "Niemals rufen und dann etwas Unangenehmes tun."},
|
||
{"id": "gk_fuss", "name": "Fuß (Leinenführigkeit)", "kat": "Grundkommando", "schwierigkeit": "Fortg. Anfänger", "alter": "Ab 12 Wo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund läuft ruhig neben dir, Leine hängt locker durch.",
|
||
"schritte": ["Leckerli auf Hüfthöhe links halten", "Schritt vorwärts, Hund folgt", "Leine locker = sofort belohnen", "Zieht er: stehen bleiben oder Richtung wechseln"],
|
||
"tipp": "Nie mitziehen lassen — Leine locker = Vorwärtsbewegung, straff = Stopp."},
|
||
{"id": "gk_aus", "name": "Aus / Lass es", "kat": "Grundkommando", "schwierigkeit": "Anfänger", "alter": "Ab 10 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund lässt Gegenstände auf Signal los — Sicherheit und Spielen.",
|
||
"schritte": ["Hund hält Spielzeug", "Leckerli vor Nase → er lässt los", "Markerwort + Leckerli", "Gegenstand zurückgeben"],
|
||
"tipp": "Immer zurückgeben → Hund lernt: Loslassen lohnt sich."},
|
||
{"id": "gk_warte", "name": "Warte", "kat": "Grundkommando", "schwierigkeit": "Anfänger", "alter": "Ab 10 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Kurzes Innehalten vor Tür, Futter, Treppe — Impulskontrolle im Alltag.",
|
||
"schritte": ["Futterschüssel abdecken wenn Hund stürmt", "Schüssel freigeben sobald er wartet", "'Okay' als Freigabe", "An Türschwellen und Autoausstieg üben"],
|
||
"tipp": "Immer mit Freigabe beenden — Hund muss wissen wann er darf."},
|
||
{"id": "gk_steh", "name": "Steh", "kat": "Grundkommando", "schwierigkeit": "Anfänger", "alter": "Ab 12 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund bleibt im Stand — praktisch beim Tierarzt, Bürsten, Pfoten abwischen.",
|
||
"schritte": ["Hund im Sitz", "Leckerli horizontal vor die Nase führen, weg vom Körper", "Hund steht auf → Markerwort + Leckerli", "Position halten üben"],
|
||
"tipp": "Wird oft vergessen — aber beim Tierarzt Gold wert!"},
|
||
{"id": "gk_rückwaerts", "name": "Rückwärtsgehen", "kat": "Grundkommando", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5 Min",
|
||
"beschreibung": "Hund geht auf Signal rückwärts — cool und nützlich bei engen Situationen.",
|
||
"schritte": ["In engem Gang stehen, langsam auf Hund zugehen", "Hund weicht zurück → Markerwort + Leckerli", "Handzeichen einführen: Finger zeigen, wegbewegen", "Schrittweise mehr Schritte"],
|
||
"tipp": "Schmaler Flur oder zwischen zwei Stühlen macht es leichter."},
|
||
{"id": "gk_apport", "name": "Apportieren", "kat": "Grundkommando", "schwierigkeit": "Mittel", "alter": "Ab 4 Mo", "dauer": "10 Min",
|
||
"beschreibung": "Hund holt geworfenes Objekt und bringt es zurück — Spielen und Sport.",
|
||
"schritte": ["Spielzeug werfen, Hund läuft hin", "Sobald er es nimmt: 'Bring!'", "Zurückkommen belohnen", "'Aus' und Gegenstand übergeben", "Schrittweise weiter werfen"],
|
||
"tipp": "Viele Hunde apportieren gern — wichtig ist das Zurückbringen und Loslassen."},
|
||
# ── TRICKS & ENTERTAINMENT ──────────────────────────────────────
|
||
{"id": "trick_pfote", "name": "Pfote / Handschlag","kat": "Trick", "schwierigkeit": "Anfänger", "alter": "Ab 12 Wo", "dauer": "3 Min",
|
||
"beschreibung": "Klassiker — sieht toll aus und ist einfach zu lernen.",
|
||
"schritte": ["Leckerli in Faust, Faust auf Kniehöhe", "Hund kratzt → Faust öffnen + belohnen", "Auf flache Hand umstellen", "Wort 'Pfote' einführen"],
|
||
"tipp": "Auf flacher Hand wird aus Kratzen ein elegantes Ablegen."},
|
||
{"id": "trick_high5", "name": "High Five", "kat": "Trick", "schwierigkeit": "Anfänger", "alter": "Ab 12 Wo", "dauer": "3 Min",
|
||
"beschreibung": "Hund klatscht dir ab — Fortsetzung des Pfoten-Tricks.",
|
||
"schritte": ["Pfote beherrscht?", "Hand senkrecht halten statt waagrecht", "Hund hebt Pfote höher → belohnen", "Wort 'High Five' einführen"],
|
||
"tipp": "Erst Pfote sicher können, dann High Five — Aufbautraining!"},
|
||
{"id": "trick_dreh", "name": "Dreh / Runde", "kat": "Trick", "schwierigkeit": "Anfänger", "alter": "Ab 12 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund dreht einen Kreis — sieht aus wie Tanzen!",
|
||
"schritte": ["Leckerli vor Nase, Kreis in der Luft führen", "Volle Drehung → Markerwort + Leckerli", "'Dreh' (links), 'Runde' (rechts)"],
|
||
"tipp": "Handbewegung schrittweise kleiner bis zum Fingerzeig."},
|
||
{"id": "trick_tod", "name": "Spiel tot", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund legt sich auf Kommando auf die Seite — der klassische Showstopper!",
|
||
"schritte": ["Hund im Platz", "Leckerli seitlich unter die Nase führen bis er kippt", "Auf Seite → Markerwort + Leckerli", "Freigabe: 'Leben'", "Wort 'Tod' oder Fingerzeig einführen"],
|
||
"tipp": "Ruhige Umgebung — viele Hunde fühlen sich erst unwohl auf der Seite."},
|
||
{"id": "trick_roll", "name": "Roll / Rollen", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund rollt sich auf dem Boden — Ausdauer-Trick mit Wow-Effekt.",
|
||
"schritte": ["'Spiel tot' beherrscht?", "Leckerli über den Rücken führen", "Hund rollt zur anderen Seite → belohnen", "Ganze Rolle → großes Lob"],
|
||
"tipp": "Auf weichem Untergrund üben — Hund rollt leichter."},
|
||
{"id": "trick_verbeugen","name": "Verbeugung", "kat": "Trick", "schwierigkeit": "Anfänger", "alter": "Ab 4 Mo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund macht einen Diener — Playbow, den Hunde von Natur aus kennen.",
|
||
"schritte": ["Hund im Stand", "Leckerli waagrecht Richtung Boden führen", "Vorderteil senkt sich, Hinterteil bleibt oben → belohnen", "Wort 'Verbeugung' einführen"],
|
||
"tipp": "Viele Hunde machen das spontan beim Spielen — einfach markieren!"},
|
||
{"id": "trick_kriech", "name": "Kriech / Robben", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund bleibt im Platz und kriecht vorwärts — sieht verrückt aus!",
|
||
"schritte": ["Hund im Platz", "Leckerli sehr flach auf dem Boden wegführen", "Hund bewegt sich vorwärts ohne aufzustehen → belohnen", "Schrittweise mehr Strecke"],
|
||
"tipp": "Unter einem Stuhl üben — natürliche Barriere zwingt ihn zu bleiben."},
|
||
{"id": "trick_slalom", "name": "Slalom Beine", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund schlängelt sich im Gehen durch deine Beine — der Augen-Trick!",
|
||
"schritte": ["Schritt machen, Leckerli führt Hund durch Beine", "Anderen Schritt, Leckerli durch andere Seite", "Immer enger, immer flüssiger", "Wort 'Slalom' einführen"],
|
||
"tipp": "Langsam gehen am Anfang — Hund braucht Zeit zum Orientieren."},
|
||
{"id": "trick_such_name","name": "Namen lernen (Spielzeug)", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund lernt Spielzeug beim Namen kennen und holt gezielt das richtige.",
|
||
"schritte": ["Ein Spielzeug: immer gleichen Namen sagen wenn er es nimmt", "Zwischen zwei Spielzeugen wählen lassen", "Gezielt das richtige holen lassen", "Weiteres Spielzeug dazufügen"],
|
||
"tipp": "Border Collies können bis zu 1000 Wörter lernen — das hier ist der Anfang!"},
|
||
{"id": "trick_schale", "name": "Leckerli auf Nase balancieren", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5 Min",
|
||
"beschreibung": "Hund hält Leckerli auf der Nase — und schnappt es auf Signal!",
|
||
"schritte": ["Hund ins Sitz, Kopf ruhig halten", "Leckerli auf Nasenrücken legen", "Kurz warten → Freigabe: Hund schnappt", "Haltezeit schrittweise verlängern"],
|
||
"tipp": "Anfangs Hand unter Kinn um Kopf zu stabilisieren."},
|
||
{"id": "trick_schublade","name": "Schublade schließen","kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10 Min",
|
||
"beschreibung": "Hund schubst Schublade mit Nase oder Pfote zu — Alltagstrick.",
|
||
"schritte": ["Haftnotiz an Schublade", "Hund berührt Notiz → belohnen", "Schublade halb offen: Berühren schließt sie → Jackpot", "Wort 'Zu' einführen"],
|
||
"tipp": "Zuerst das Berühren eines Zielobjekts trainieren (Targeting)."},
|
||
{"id": "trick_zählen", "name": "Zählen", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund klopft mit Pfote auf Boden — sieht aus wie echtes Zählen!",
|
||
"schritte": ["Pfote-Trick beherrscht?", "Pfote auf deiner Hand → Leckerli", "Hand tiefer bis Hund auf Boden klopft", "Anzahl variieren: 1x, 2x, 3x markieren"],
|
||
"tipp": "Du gibst heimlich das Signal — Publikum denkt der Hund rechnet wirklich."},
|
||
{"id": "trick_hupe", "name": "Klingel läuten", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund läutet eine Glocke — z.B. um rauszuwollen.",
|
||
"schritte": ["Glocke an Türklinke hängen", "Haftnotiz drauf, Hund berührt → belohnen", "Berühren läutet Glocke → Tür öffnen", "Nur klingeln wenn er raus will"],
|
||
"tipp": "Manche Hunde nutzen das dann selbstständig — klingeln wenn sie müssen!"},
|
||
{"id": "trick_korb", "name": "Spielzeug aufräumen", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund räumt Spielzeug in Körbe — nützlich und beeindruckend.",
|
||
"schritte": ["Apport und Aus beherrscht?", "Körb vor Hund stellen", "Spielzeug aufheben lassen → über Korb führen → Aus → belohnen", "Schrittweise mehrere Objekte"],
|
||
"tipp": "Einer der Tricks der am meisten Leute beeindruckt!"},
|
||
{"id": "trick_umarmung", "name": "Umarmung / Herz", "kat": "Trick", "schwierigkeit": "Anfänger", "alter": "Ab 4 Mo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund legt Kopf oder Vorderpfoten auf dich — für süße Fotos!",
|
||
"schritte": ["Auf Knie hinhocken", "Leckerli hinter deinen Nacken führen", "Hund legt Pfoten auf Schultern um dranzukommen", "Markerwort + Leckerli + Kamera bereit!"],
|
||
"tipp": "Perfekt für Insta-Content — immer einen Fotografen parat haben!"},
|
||
{"id": "trick_licht", "name": "Licht ausschalten", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund drückt Lichtschalter — praktisch und beeindruckend!",
|
||
"schritte": ["Targeting trainieren (Nase/Pfote berührt Ziel)", "Zielobjekt an Lichtschalter kleben", "Berühren löst Licht aus → Jackpot", "Wort 'Licht aus' einführen"],
|
||
"tipp": "Hund braucht dafür etwas Körpergröße — bei kleinen Hunden Schalter tiefer setzen."},
|
||
# ── PROBLEMVERHALTEN & ALLTAG ───────────────────────────────────
|
||
{"id": "pb_springen", "name": "Nicht springen", "kat": "Problemverhalten","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "Jede Begrüßung",
|
||
"beschreibung": "Hund begrüßt mit allen vier Pfoten am Boden.",
|
||
"schritte": ["Springt er: keine Reaktion", "Vier Pfoten unten → sofort belohnen", "Alle Haushaltsmitglieder gleich reagieren"],
|
||
"tipp": "Einmal springen lassen = wochenlanger Rückschritt."},
|
||
{"id": "pb_allein", "name": "Alleine bleiben", "kat": "Problemverhalten","schwierigkeit": "Mittel", "alter": "Ab 10 Wo", "dauer": "Mehrmals täglich",
|
||
"beschreibung": "Hund bleibt ruhig allein — ohne Stress, Bellen oder Zerstören.",
|
||
"schritte": ["Kong füllen", "10 Sek raus → ruhig rein", "Zeit schrittweise erhöhen", "Kommen und Gehen normalisieren"],
|
||
"tipp": "Welpen max. 1–2 Stunden, Erwachsene max. 4–6 Stunden."},
|
||
{"id": "pb_bellen", "name": "Weniger Bellen", "kat": "Problemverhalten","schwierigkeit": "Mittel", "alter": "Ab 8 Wo", "dauer": "5–10 Min täglich",
|
||
"beschreibung": "Hund beruhigt sich auf Signal — kein übermäßiges Kläffen.",
|
||
"schritte": ["Ursache kennen: Alarm, Frust, Aufmerksamkeit, Angst", "Aufmerksamkeits-Bellen ignorieren", "Pause abwarten → markieren + belohnen", "'Ruhig' einführen"],
|
||
"tipp": "Nie schreien — der Hund denkt du bellst mit!"},
|
||
{"id": "pb_leine", "name": "Nicht an Leine ziehen", "kat": "Problemverhalten","schwierigkeit": "Mittel", "alter": "Ab 12 Wo", "dauer": "Jeder Spaziergang",
|
||
"beschreibung": "Hund zieht nicht — Spaziergang wird für beide entspannter.",
|
||
"schritte": ["Ruhigen Start üben", "Zieht er: stehen bleiben", "Oder Richtung wechseln", "Leine locker = Leckerli + Vorwärtsbewegung"],
|
||
"tipp": "Brustgeschirr mit vorderer Befestigung hilft bei starken Ziehern."},
|
||
{"id": "pb_desensib", "name": "Desensibilisierung","kat": "Problemverhalten","schwierigkeit": "Fortg. Anfänger", "alter": "Ab 12 Wo", "dauer": "5–15 Min",
|
||
"beschreibung": "Hund bleibt unter Reizen (Hunde, Menschen, Fahrräder) entspannt.",
|
||
"schritte": ["Trigger identifizieren", "Schwellenabstand ermitteln", "Reiz zeigen → sofort hochwertiges Leckerli", "Distanz ganz langsam verringern"],
|
||
"tipp": "Bei Überreaktion: ruhig mehr Abstand, keine Strafe."},
|
||
{"id": "pb_auto", "name": "Autofahren / Ruhig im Auto", "kat": "Problemverhalten","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "Schrittweise",
|
||
"beschreibung": "Hund fährt entspannt mit — kein Hecheln, Bellen oder Erbrechen.",
|
||
"schritte": ["Auto als positiven Ort einführen: Leckerlis im stehenden Auto", "Kurze Fahrten (2 Min) → belohnen", "Zeit schrittweise erhöhen", "Immer mit leerem Magen fahren"],
|
||
"tipp": "Gesicherter Transport (Gurt, Box) schützt Hund und Fahrer."},
|
||
{"id": "pb_fremde", "name": "Ruhig bei Fremden", "kat": "Problemverhalten","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "Täglich",
|
||
"beschreibung": "Hund begrüßt Fremde freundlich aber ruhig.",
|
||
"schritte": ["Kontrollierte Begegnungen üben", "Hund darf Fremde begrüßen wenn ruhig", "Springen und Zappeln ignorieren", "Fremde geben Leckerlis wenn Hund ruhig ist"],
|
||
"tipp": "Sozialisierungsfenster bis 16 Wochen nutzen!"},
|
||
{"id": "pb_kinder", "name": "Hund und Kinder", "kat": "Problemverhalten","schwierigkeit": "Mittel", "alter": "Ab 8 Wo", "dauer": "Täglich üben",
|
||
"beschreibung": "Hund verhält sich sicher und ruhig rund um Kinder.",
|
||
"schritte": ["Ruhige, positive Begegnungen arrangieren", "Keine Aufregung fördern", "Hund hat immer Rückzugsmöglichkeit", "Kinder: nie rennen oder schreien beim Hund"],
|
||
"tipp": "Hund und Kind nie unbeaufsichtigt lassen — egal wie brav der Hund ist."},
|
||
{"id": "pb_box", "name": "Box-Training / Körbchen", "kat": "Problemverhalten","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "5–10 Min täglich",
|
||
"beschreibung": "Hund liebt seine Box als sicheren Rückzugsort.",
|
||
"schritte": ["Box mit Leckereien bestücken", "Hund geht freiwillig rein → feiern", "Tür erst nach Gewöhnung schließen", "Box nie zur Strafe nutzen"],
|
||
"tipp": "Box ist Rückzugsort, kein Gefängnis — dort nie stören."},
|
||
{"id": "pb_impulskontrolle", "name": "Impulskontrolle", "kat": "Problemverhalten","schwierigkeit": "Anfänger", "alter": "Ab 10 Wo", "dauer": "5 Min täglich",
|
||
"beschreibung": "Hund wartet und handelt nicht impulsiv — z.B. Leckerli nicht sofort schnappen.",
|
||
"schritte": ["Leckerli auf Handfläche: Hand schließen wenn er schnappt", "Sobald er wartet: Hand öffnen + belohnen", "Leckerli auf Boden: 'Warte' bis Freigabe", "Zeit variieren"],
|
||
"tipp": "Impulskontrolle ist die Basis für fast alle anderen Übungen."},
|
||
{"id": "pb_maulkorb", "name": "Maulkorb-Training", "kat": "Problemverhalten","schwierigkeit": "Anfänger", "alter": "Jedes Alter","dauer": "10–15 Min",
|
||
"beschreibung": "Hund trägt Maulkorb entspannt — für Notfall, Tierarzt, Bus.",
|
||
"schritte": ["Maulkorb zeigen: Leckerli darin → Hund steckt Nase rein", "Nur am Maulkorb füttern", "Maulkorb kurz anlassen → belohnen", "Zeit schrittweise erhöhen"],
|
||
"tipp": "Präventiv trainieren — im Notfall ist es zu spät."},
|
||
{"id": "pb_begegnung", "name": "Hundebegegnungen an der Leine", "kat": "Problemverhalten","schwierigkeit": "Fortg.","alter": "Ab 3 Mo", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund reagiert entspannt auf andere Hunde an der Leine.",
|
||
"schritte": ["Großen Abstand halten", "Anderen Hund zeigen → Leckerli", "Hund schaut → du lobst", "Abstand sehr langsam verringern"],
|
||
"tipp": "Nie Hunde auf Leine frontal aufeinander treffen lassen."},
|
||
# ── MENTALE AUSLASTUNG ──────────────────────────────────────────
|
||
{"id": "mental_nase", "name": "Nasenarbeit Basis", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "5–10 Min",
|
||
"beschreibung": "10 Min Suchen ermüdet mehr als 1 Stunde Spaziergang!",
|
||
"schritte": ["Leckerli unter Becher verstecken", "Hund darf suchen → findet er es: belohnen", "3 Becher, 1 mit Leckerli", "Später im Gras verstecken: 'Such!'"],
|
||
"tipp": "Perfekt für Regentage."},
|
||
{"id": "mental_intelligenz", "name": "Intelligenzspielzeug", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "Ab 10 Wo", "dauer": "10–20 Min",
|
||
"beschreibung": "Hund knobelt selbstständig — Futter holen durch Schieben, Drehen, Heben.",
|
||
"schritte": ["Einfaches Level zuerst", "Hund beobachten lassen wie Futter herauskommt", "Nicht helfen — Frustration kurz aushalten lassen", "Schwierigkeit steigern"],
|
||
"tipp": "Vorsicht Sucht: manche Hunde können nicht aufhören!"},
|
||
{"id": "mental_schnueffeln", "name": "Schnüffelrasen / Futtermatte", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "Ab 8 Wo","dauer": "5–10 Min",
|
||
"beschreibung": "Futter im Rasen/Matte verstecken — Nase an, Gehirn aus.",
|
||
"schritte": ["Futtermatte vorbereiten", "Hund mit 'Such' freigeben", "Ruhig beobachten", "Als Mahlzeit-Ersatz nutzen"],
|
||
"tipp": "Verlangsamt Fressen und beruhigt — ideal nach Aufregung."},
|
||
{"id": "mental_tracking","name": "Mantrailing / Fährtenarbeit", "kat": "Mentale Auslastung","schwierigkeit": "Fortg.","alter": "Ab 6 Mo", "dauer": "20–30 Min",
|
||
"beschreibung": "Hund folgt einer Menschenfährte — uralter Instinkt wiederbeleben.",
|
||
"schritte": ["Kurze Fährte legen (5 Meter)", "Leckerlis auf Fährte", "'Such' − Hund folgt Nase", "Schrittweise: länger, älter, schwieriger"],
|
||
"tipp": "Erschöpft selbst große aktive Hunde komplett."},
|
||
{"id": "mental_dummy", "name": "Dummy-Training Basis","kat": "Mentale Auslastung","schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "15–20 Min",
|
||
"beschreibung": "Hund sucht geworfenes Dummy und bringt es zurück — Jagdhund-Instinkt.",
|
||
"schritte": ["Dummy zeigen, Begeisterung wecken", "Kurz werfen: 3–5 Meter", "Hund holt → Rückruf", "Dummy übergeben → Leckerli"],
|
||
"tipp": "Wasserdummys für See-Variante — Hunde lieben das!"},
|
||
{"id": "mental_tricks_sequenz", "name": "Trick-Sequenzen", "kat": "Mentale Auslastung","schwierigkeit": "Fortg.","alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund führt mehrere Tricks hintereinander durch — Konzentration pur.",
|
||
"schritte": ["Zwei bekannte Tricks verbinden: Sitz → Pfote", "Immer kürzer werdende Pausen", "Drei Tricks aneinanderreihen", "Als Show präsentieren"],
|
||
"tipp": "Trainiert Konzentration und Flexibilität gleichzeitig."},
|
||
{"id": "mental_zieltraining", "name": "Zieltraining (Targeting)", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "Ab 10 Wo","dauer": "5 Min",
|
||
"beschreibung": "Hund berührt ein Zielobjekt mit Nase oder Pfote — Basis für viele Tricks.",
|
||
"schritte": ["Hand vor Hund halten", "Hund berührt sie mit Nase → Markerwort + Leckerli", "Auf Stab oder Post-It übertragen", "Ziel bewegen: Hund folgt"],
|
||
"tipp": "Schlüssel für Schublade, Licht, Slam Dunk und Co."},
|
||
{"id": "mental_freiarbeit", "name": "Freiarbeit / Shaping", "kat": "Mentale Auslastung","schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund findet selbst heraus was belohnt wird — stärkt Eigeninitiative.",
|
||
"schritte": ["Neues Objekt in Raum stellen", "Jede Interaktion markieren + belohnen", "Schrittweise nur bestimmte Aktionen belohnen", "Hund erfindet Tricks selbst"],
|
||
"tipp": "Manche Hunde flippen aus vor Freude — macht süchtig!"},
|
||
# ── KÖRPERPFLEGE & HANDLING ─────────────────────────────────────
|
||
{"id": "body_pfoten", "name": "Pfoten anfassen / reinigen", "kat": "Körperpflege","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "3–5 Min täglich",
|
||
"beschreibung": "Hund lässt Pfoten anfassen, reinigen und Krallen schneiden.",
|
||
"schritte": ["Pfote kurz berühren → belohnen", "Länger halten → belohnen", "Zwischen Zehen fassen", "Krallenschneider zeigen bevor er benutzt wird"],
|
||
"tipp": "Jeden Tag ein paar Sekunden — besser als einmal im Monat kämpfen."},
|
||
{"id": "body_bürsten", "name": "Bürsten / Fellpflege", "kat": "Körperpflege","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "2–5 Min täglich",
|
||
"beschreibung": "Hund steht ruhig beim Bürsten — ohne Weglaufen oder Schnappen.",
|
||
"schritte": ["Bürste zeigen: Leckerli daneben", "Kurz bürsten → belohnen", "Empfindliche Stellen zuletzt", "Positive Assoziation mit Bürste aufbauen"],
|
||
"tipp": "Gleiche Routine täglich → Hund weiß was kommt und entspannt sich."},
|
||
{"id": "body_ohren", "name": "Ohren reinigen", "kat": "Körperpflege","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund lässt Ohren reinigen — Gesundheitscheck ohne Stress.",
|
||
"schritte": ["Ohr kurz anfassen → belohnen", "Ohr hochhalten → belohnen", "Tuch zeigen: Leckerli drauf", "Kurz reinigen → große Belohnung"],
|
||
"tipp": "Bevor es schmerzt trainieren — nicht wenn Ohr schon entzündet ist."},
|
||
{"id": "body_zähne", "name": "Zähne putzen", "kat": "Körperpflege","schwierigkeit": "Mittel", "alter": "Ab 8 Wo", "dauer": "2–5 Min täglich",
|
||
"beschreibung": "Hund lässt Zähne putzen — spart teure Narkosen beim Tierarzt.",
|
||
"schritte": ["Finger in Mund → belohnen", "Hundezahnpasta zeigen (essbar!)", "Finger mit Paste reiben", "Zahnbürste einführen"],
|
||
"tipp": "Zahnpasta für Hunde → lecker für Hunde, sicher zu schlucken."},
|
||
{"id": "body_tierarzt", "name": "Tierarzt-Training", "kat": "Körperpflege","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund ist beim Tierarzt kooperativ — kein Trauma, kein Stress.",
|
||
"schritte": ["Tisch von unten anfassen → belohnen", "Auf Tisch/Waage stellen → belohnen", "Körper überall berühren", "Bekannte Menschen 'untersuchen' lassen"],
|
||
"tipp": "Gesunde Besuche beim Tierarzt nur zum Leckerli holen."},
|
||
{"id": "body_staubsauger","name": "Staubsauger / Geräusche", "kat": "Körperpflege","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund reagiert entspannt auf Haushaltsgeräusche.",
|
||
"schritte": ["Staubsauger ausgeschaltet zeigen → Leckerli", "Kurz einschalten, weiter weg", "Leckerlis während Lärm", "Schrittweise näher / länger"],
|
||
"tipp": "Sozialisierungsfenster nutzen — unter 16 Wochen am einfachsten."},
|
||
{"id": "body_handling", "name": "Allgemeines Handling", "kat": "Körperpflege","schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "5 Min täglich",
|
||
"beschreibung": "Hund lässt sich überall anfassen — Maul, Pfoten, Bauch, Schwanz.",
|
||
"schritte": ["Täglich verschiedene Körperstellen anfassen", "Immer mit Leckerli verbinden", "Schnell → langsam → fest", "Ungewohnte Geräusche dabei machen"],
|
||
"tipp": "Basis für alles — gut angepackter Hund ist ein entspannter Hund."},
|
||
# ── HUNDESPORT BASICS ───────────────────────────────────────────
|
||
{"id": "sport_sprung", "name": "Sprung / Hindernis", "kat": "Hundesport", "schwierigkeit": "Anfänger", "alter": "Ab 1 Jahr", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund springt auf Signal über Hindernisse — Agility-Basis.",
|
||
"schritte": ["Hindernis flach am Boden", "Hund drüber locken mit Leckerli", "Wort 'Hopp' einführen", "Höhe sehr schrittweise erhöhen"],
|
||
"tipp": "Unter 12 Monate: keine Sprünge — Gelenke noch nicht ausgewachsen."},
|
||
{"id": "sport_tunnel", "name": "Tunnel", "kat": "Hundesport", "schwierigkeit": "Anfänger", "alter": "Ab 8 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund läuft durch Tunnel — Agility-Klassiker.",
|
||
"schritte": ["Kurzen Tunnel zeigen (zusammengefaltet)", "Hund reinlocken mit Leckerli", "Am anderen Ende warten und rufen", "Tunnel schrittweise strecken"],
|
||
"tipp": "Die meisten Hunde lieben Tunnel — schnell gelernt!"},
|
||
{"id": "sport_weave", "name": "Slalom / Weave Poles", "kat": "Hundesport", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund schlängelt sich durch Stangen — anspruchsvollste Agility-Übung.",
|
||
"schritte": ["2 Stangen: durch die Mitte führen", "Stangen schrittweise enger", "Tempo erhöhen", "6, dann 12 Stangen"],
|
||
"tipp": "Dauert Monate bis es sitzt — Geduld zahlt sich aus!"},
|
||
{"id": "sport_trickdog", "name": "Trickdogging Basics", "kat": "Hundesport", "schwierigkeit": "Mittel", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Choreographierte Trick-Abfolgen zu Musik — Hund und Mensch als Team.",
|
||
"schritte": ["5–6 sichere Tricks auswählen", "In Reihenfolge üben", "Musik dazu abspielen", "Auf Rhythmus achten"],
|
||
"tipp": "Videos davon machen — sieht immer besser aus als man denkt!"},
|
||
{"id": "sport_balance", "name": "Balance / Balanceboard", "kat": "Hundesport", "schwierigkeit": "Mittel", "alter": "Ab 1 Jahr", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund balanciert auf wackeligen Unterlagen — Körperbewusstsein stärken.",
|
||
"schritte": ["Instabiles Polster → Vorderpfoten drauf", "Alle vier Pfoten", "Balanceboard einführen", "Bewegung auf dem Board belohnen"],
|
||
"tipp": "Stärkt Koordination und Muskulatur — gut für Sportler."},
|
||
{"id": "sport_hürde_körper", "name": "Hürde mit Körper", "kat": "Hundesport", "schwierigkeit": "Anfänger", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund springt über dein ausgestrecktes Bein oder deinen Arm.",
|
||
"schritte": ["Bein flach auf Boden → Hund drüber locken", "Bein höher", "Arm als Hürde", "Tempo einbauen"],
|
||
"tipp": "Kein Equipment nötig — überall möglich!"},
|
||
# ── ERWEITERTE GRUNDKOMMANDOS ────────────────────────────────────
|
||
{"id": "gk_links_rechts", "name": "Links / Rechts lernen", "kat": "Grundkommando", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10 Min",
|
||
"beschreibung": "Hund unterscheidet Links und Rechts auf Kommando.",
|
||
"schritte": ["Immer gleiches Leckerli-Signal für Links", "Anderem Signal für Rechts", "Konsequent gleiche Hand = gleiche Richtung", "Schrittweise ohne Handbewegung"],
|
||
"tipp": "Braucht Zeit — aber Wow-Faktor beim Publikum ist enorm."},
|
||
{"id": "gk_freifolge", "name": "Freifolge (ohne Leine)", "kat": "Grundkommando", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund geht neben dir ohne Leine auf Signal — höchste Verlässlichkeit.",
|
||
"schritte": ["Fuß an Leine perfekt?", "Leine locker lassen bis sie kaum Spannung hat", "Leine hängen lassen, dann ablegen", "Schrittweise offenere Umgebungen"],
|
||
"tipp": "Nur in sicherer Umgebung üben — nie auf Straße oder Hundewiese testen."},
|
||
{"id": "gk_schleich", "name": "Schleichen", "kat": "Grundkommando", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund bewegt sich in geduckter Haltung vorwärts — Geheimdienstmodus!",
|
||
"schritte": ["Hund im Platz", "Leckerli sehr tief halten und wegführen", "Hund kriecht vorwärts", "'Schleich' Wort einführen"],
|
||
"tipp": "Tunnelübung hilft als natürliche Führungshilfe."},
|
||
{"id": "gk_platzmit", "name": "Platz mit Distanz", "kat": "Grundkommando", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10 Min",
|
||
"beschreibung": "Hund geht auf Kommando von weitem ins Platz — ohne hinzugehen.",
|
||
"schritte": ["Platz auf 1 Meter — Hund geht selbst hin", "Schrittweise weiter weg", "Handzeichen aus Entfernung", "Richtungswechsel einbauen"],
|
||
"tipp": "Braucht gefestigtes Platz und Bleib zuerst."},
|
||
{"id": "gk_stop", "name": "Stop / Stehenbleiben", "kat": "Grundkommando", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund bleibt auf Signal sofort stehen — auch aus der Bewegung.",
|
||
"schritte": ["Hund neben dir gehen", "Plötzlich stoppen, Leckerli halten", "Hund stoppt → belohnen", "Wort 'Stop' einführen, dann Entfernung aufbauen"],
|
||
"tipp": "Wichtig für Sicherheit — z.B. kurz vor einer Straße."},
|
||
# ── ERWEITERTE TRICKS ────────────────────────────────────────────
|
||
{"id": "trick_kopfsenken", "name": "Kopf senken / Schämen", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund legt Kopf auf die Pfoten — süßester Trick überhaupt!",
|
||
"schritte": ["Hund im Platz", "Leckerli auf Pfoten legen", "Hund schaut runter → Markerwort", "Wort 'Schäm dich' oder 'Kopf runter'"],
|
||
"tipp": "Für Instagram-Fotos unschlagbar schön."},
|
||
{"id": "trick_spiegeln", "name": "Spiegeln / Mirrorwork", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund ahmt deine Bewegungen nach — Trickdogging-Highlight.",
|
||
"schritte": ["Vor Hund stehen, Schritt links → Hund folgt rechts", "Belohnen wenn er spiegelt", "Verschiedene Bewegungen einbauen", "Rhythmisch werden"],
|
||
"tipp": "Basis für Tanzchoreographien."},
|
||
{"id": "trick_welle", "name": "Winke / Welle", "kat": "Trick", "schwierigkeit": "Anfänger", "alter": "Ab 12 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund hebt Pfote und winkt — super für Videos und Fotos!",
|
||
"schritte": ["Pfote beherrscht?", "Hand hoch halten statt waagrecht", "Hund hebt Pfote höher, wackelt", "'Winke' oder 'Tschüss' einführen"],
|
||
"tipp": "Kombi mit 'High Five' macht super Video-Content."},
|
||
{"id": "trick_skateboard","name": "Skateboard fahren", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–20 Min",
|
||
"beschreibung": "Hund schiebt und fährt ein Skateboard — TikTok-Garant!",
|
||
"schritte": ["Board zeigen: Leckerli drauf → Pfoten drauf", "Alle 4 Pfoten auf Board", "Board leicht schieben", "Hund schiebt selbst mit Fuß ab"],
|
||
"tipp": "Bulldog-Rassen lieben das angeblich genetisch..."},
|
||
{"id": "trick_basketball","name": "Basketball / Dunking", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund wirft Ball in Korb — beeindruckendster Trick für Zuschauer.",
|
||
"schritte": ["Apport und Targeting beherrscht?", "Ball in den Korb targeten", "Ball aufnehmen und zu Korb gehen", "Ball über Korbrand fallen lassen"],
|
||
"tipp": "Körpergröße muss zum Korb passen."},
|
||
{"id": "trick_kreativ", "name": "101-Ding-Spiel", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "10 Min",
|
||
"beschreibung": "Hund erfindet spontan Aktionen mit einem Objekt — 101 Wege sind möglich!",
|
||
"schritte": ["Neues Objekt vor Hund stellen", "Jede Interaktion markieren und belohnen", "Nur noch bestimmte Aktionen belohnen", "Hund steigert Kreativität"],
|
||
"tipp": "Macht Hunde brillant — stärkt Eigeninitiative wie nichts sonst."},
|
||
{"id": "trick_koffer", "name": "Koffer ziehen", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund zieht ein Spielzeug oder kleines Objekt hinter sich her.",
|
||
"schritte": ["Seil an Objekt befestigen", "Hund nimmt Seil ins Maul", "Rückwärtsgehen einbauen", "Objekt zieht nach"],
|
||
"tipp": "Mit Apportier-Hund einfach umzusetzen."},
|
||
{"id": "trick_over_arm", "name": "Über den Arm klettern", "kat": "Trick", "schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund klettert über deinen ausgestreckten Arm — Freestyle-Move!",
|
||
"schritte": ["Arm flach auf Boden", "Hund drüber locken", "Arm höher", "Hund klettert elegant drüber"],
|
||
"tipp": "Auch rückwärts möglich — sieht spektakulär aus."},
|
||
{"id": "trick_beinzwischen", "name": "Zwischen den Beinen stehen", "kat": "Trick", "schwierigkeit": "Anfänger", "alter": "Ab 12 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund steht ruhig zwischen deinen Beinen — Foto-Klassiker!",
|
||
"schritte": ["Breitbeinig stehen", "Leckerli zwischen die Beine führen", "Hund folgt und steht drin", "Wort 'Zwischen' einführen"],
|
||
"tipp": "Basis für viele Weiterführungen: z.B. Slalom aus Stehposition."},
|
||
{"id": "trick_küsschen", "name": "Küsschen geben", "kat": "Trick", "schwierigkeit": "Anfänger", "alter": "Ab 12 Wo", "dauer": "3 Min",
|
||
"beschreibung": "Hund leckt auf Kommando die Wange — für süße Fotos!",
|
||
"schritte": ["Leckerli-Reste auf Wange kleben", "Hund leckt → Markerwort", "Ohne Köder wiederholen", "'Küsschen' einführen"],
|
||
"tipp": "Hygiene beachten — nach Spaziergang zuerst Maul reinigen 😄"},
|
||
{"id": "trick_twist_jump", "name": "Sprung mit Drehung", "kat": "Trick", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "10 Min",
|
||
"beschreibung": "Hund springt hoch und dreht sich in der Luft — Wow-Faktor!",
|
||
"schritte": ["Dreh-Trick beherrscht?", "Auf Hinterbeine stellen üben", "Beim Aufstehen Dreh-Signal geben", "Schrittweise mehr Höhe"],
|
||
"tipp": "Nur für agile Hunde — Gelenke schonen!"},
|
||
# ── ERWEITERTE PROBLEMVERHALTEN ──────────────────────────────────
|
||
{"id": "pb_gewitter", "name": "Angst vor Gewitter/Feuerwerk", "kat": "Problemverhalten","schwierigkeit": "Mittel", "alter": "Jedes Alter","dauer": "Langfristig",
|
||
"beschreibung": "Hund bleibt bei Lärm entspannt — kein Zittern, Verstecken, Panik.",
|
||
"schritte": ["Geräusche aufnehmen, sehr leise abspielen", "Leckerlis während Geräusch", "Lautstärke sehr langsam erhöhen", "Sicheren Rückzugsort anbieten"],
|
||
"tipp": "Thundershirt kann kurzfristig helfen — kein Ersatz für Training."},
|
||
{"id": "pb_jagd", "name": "Jagdinstinkt kontrollieren", "kat": "Problemverhalten","schwierigkeit": "Fortg.", "alter": "Ab 6 Mo", "dauer": "Dauerprojekt",
|
||
"beschreibung": "Hund kann Jagdtrieb unterdrücken — Rückruf auch bei Wild.",
|
||
"schritte": ["Impuls-Kontrolle sehr fest verankern", "Rückruf mit Hochwertigem belohnen", "An der Schleppleine üben", "Kontrolliertes Zeigen von Wild mit Ablenkung"],
|
||
"tipp": "Manche Rassen haben sehr hohen Jagdtrieb — professionelle Hilfe sinnvoll."},
|
||
{"id": "pb_ressourcen", "name": "Ressourcen-Verteidigung", "kat": "Problemverhalten","schwierigkeit": "Fortg.", "alter": "Ab 8 Wo", "dauer": "Langfristig",
|
||
"beschreibung": "Hund knurrt nicht über Futter oder Spielzeug — 'Tauschhandel' lernen.",
|
||
"schritte": ["Beim Fressen Hand neben Schüssel legen", "Etwas Besseres tauschen gegen Schüssel", "Nie Schüssel wegnehmen ohne Gegenleistung", "Frühzeitig anfangen"],
|
||
"tipp": "Niemals mit Hund kämpfen — Veterinär-Verhaltenstherapeut hinzuziehen."},
|
||
{"id": "pb_beißhemmung", "name": "Beißhemmung (Welpe)", "kat": "Problemverhalten","schwierigkeit": "Anfänger", "alter": "8–16 Wochen","dauer": "Täglich",
|
||
"beschreibung": "Welpe lernt: Zähne tun weh — sanft sein mit Menschen.",
|
||
"schritte": ["Zähne an Haut → 'Au!' und Spielpause", "Sanfter Druck → weiter spielen", "Spielzeug immer bereit als Ersatz", "Konsequenz aller Haushaltsmitglieder"],
|
||
"tipp": "Zeitfenster ist 8–16 Wochen — danach viel schwerer zu lernen."},
|
||
{"id": "pb_trennungsangst", "name": "Trennungsangst intensiv","kat": "Problemverhalten","schwierigkeit": "Fortg.", "alter": "Jedes Alter","dauer": "Wochen bis Monate",
|
||
"beschreibung": "Hund entspannt sich wenn Besitzer geht — kein Stress mehr.",
|
||
"schritte": ["Körperweitergabe-Test: reagiert er auf Körpersprache vor dem Gehen?", "Abschiedssignale entkoppeln", "Kurze Abwesenheiten unter Stressschwelle", "Sehr langsam steigern"],
|
||
"tipp": "Echte Trennungsangst braucht Veterinär-Verhaltenstherapeuten."},
|
||
{"id": "pb_klingel", "name": "Ruhig bei Klingel / Besuch", "kat": "Problemverhalten","schwierigkeit": "Mittel", "alter": "Ab 10 Wo", "dauer": "Täglich",
|
||
"beschreibung": "Hund bellt nicht oder kurz bei Klingel und bleibt ruhig.",
|
||
"schritte": ["Klingel-Sound am Handy: Leckerli bei jedem Klingeln", "Hund ins Körbchen schicken auf Klingel", "Echter Besuch: im Körbchen bleiben bis ruhig", "Freigabe: Hund darf begrüßen"],
|
||
"tipp": "Einheitliche Reaktion aller Haushaltsmitglieder ist entscheidend."},
|
||
{"id": "pb_graben", "name": "Nicht graben", "kat": "Problemverhalten","schwierigkeit": "Mittel", "alter": "Ab 10 Wo", "dauer": "Dauerprojekt",
|
||
"beschreibung": "Hund gräbt nicht im Garten — oder nur an erlaubten Stellen.",
|
||
"schritte": ["Erlaubte Grabstelle einrichten (Sandkasten)", "Leckerlis dort verstecken", "Unerwünschtes Graben: Hund umleiten", "Nie bestrafen — nur umleiten"],
|
||
"tipp": "Terrier graben genetisch — ihnen eine eigene Grabstelle geben."},
|
||
{"id": "pb_reaktivität", "name": "Reaktivität reduzieren", "kat": "Problemverhalten","schwierigkeit": "Fortg.", "alter": "Ab 12 Wo", "dauer": "Monate",
|
||
"beschreibung": "Hund überreagiert nicht auf Reize — ruhiger an Leine und allgemein.",
|
||
"schritte": ["Trigger-Liste erstellen", "Abstand zu jedem Trigger bis unter Schwelle", "Entspanntes Verhalten beim Trigger stark belohnen", "Reize langsam aufbauen"],
|
||
"tipp": "BAT (Behavior Adjustment Training) sehr effektiv — Videos auf YouTube."},
|
||
# ── ERWEITERTE MENTALE AUSLASTUNG ────────────────────────────────
|
||
{"id": "mental_verstecken", "name": "Verstecken spielen", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "Ab 4 Mo", "dauer": "10–20 Min",
|
||
"beschreibung": "Menschen im Haus oder draußen suchen — macht riesigen Spaß!",
|
||
"schritte": ["Hund kurz ablenken", "Verstecken", "'Hund, such!'", "Gefunden = riesige Belohnung"],
|
||
"tipp": "Kinder lieben es als Suchpartner — Hund ist begeistert."},
|
||
{"id": "mental_welcher_becher", "name": "Welcher Becher?", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "Ab 8 Wo", "dauer": "5–10 Min",
|
||
"beschreibung": "Shell-Spiel für Hunde — Leckerli unter einem von drei Bechern finden.",
|
||
"schritte": ["Hund schaut: Leckerli unter Becher", "Becher nicht bewegen: Hund zeigt richtigen", "Becher langsam tauschen", "Tempo erhöhen"],
|
||
"tipp": "Hunde haben phänomenales Gedächtnis — kaum zu überlisten!"},
|
||
{"id": "mental_geräusche", "name": "Geräusch-Sozialisierung", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "8–16 Wochen","dauer": "Täglich 5 Min",
|
||
"beschreibung": "Welpe lernt Stadtlärm, Kinderlärm, Staubsauger — für entspanntes Leben.",
|
||
"schritte": ["Geräusch-CDs für Welpen abspielen", "Sehr leise beginnen", "Leckerlis während Geräusch", "Echte Situationen aufsuchen"],
|
||
"tipp": "Sozialisierungsfenster bis 16 Wochen — jetzt oder nie!"},
|
||
{"id": "mental_trick_routine", "name": "Tägliche Trick-Routine","kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "Ab 6 Mo", "dauer": "5 Min täglich",
|
||
"beschreibung": "5 Minuten Tricks täglich — Hund bleibt fit im Kopf und baut Bindung auf.",
|
||
"schritte": ["3–5 bekannte Tricks auswählen", "Täglich gleiche Zeit", "Variieren: heute dies, morgen das", "Neuen Trick pro Woche dazu"],
|
||
"tipp": "Besser 5 Min täglich als 1 Stunde einmal die Woche."},
|
||
{"id": "mental_knobelbox", "name": "DIY Knobelbox", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "Ab 10 Wo", "dauer": "10–15 Min",
|
||
"beschreibung": "Selbstgebastelte Rätselboxen aus Pappröhren, Dosen, Flaschen — kostet nichts!",
|
||
"schritte": ["Leckerlis in Klopapierrolle einrollen, Enden umknicken", "Mehrere Rollen in Karton", "Hund soll Leckerlis befreien", "Schwierigkeit erhöhen"],
|
||
"tipp": "DIY-Inspirationen auf Pinterest oder YouTube finden."},
|
||
{"id": "mental_social", "name": "Hunde-Kindergarten / Sozialtraining", "kat": "Mentale Auslastung","schwierigkeit": "Anfänger","alter": "8–16 Wochen","dauer": "Wöchentlich",
|
||
"beschreibung": "Welpe spielt mit anderen Welpen — wichtigste Sozialisierung überhaupt.",
|
||
"schritte": ["Zertifizierten Welpen-Kurs suchen", "Impfschutz vorher prüfen", "Spielgruppen nach Größe getrennt", "Regelmäßig hingehen bis 16 Wochen"],
|
||
"tipp": "Kein Welpen-Kurs = größtes Trainingsversäumnis überhaupt."},
|
||
{"id": "mental_geruch_id", "name": "Geruchserkennung", "kat": "Mentale Auslastung","schwierigkeit": "Mittel", "alter": "Ab 6 Mo", "dauer": "10–15 Min",
|
||
"beschreibung": "Hund lernt bestimmte Gerüche zu erkennen — Basis für professionelle Arbeit.",
|
||
"schritte": ["Wattestäbchen mit Gewürzduft", "An Box befestigen", "Hund zeigt Box an → belohnen", "Andere Duftstoffe als Ablenkung dazu"],
|
||
"tipp": "Hundenase riecht 10.000x besser als Menschennase — nutze das!"},
|
||
# ── ERWEITERTE KÖRPERPFLEGE ──────────────────────────────────────
|
||
{"id": "body_bad", "name": "Baden / Duschen", "kat": "Körperpflege", "schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "Schrittweise",
|
||
"beschreibung": "Hund steht ruhig in Wanne oder Dusche — kein Drama mehr.",
|
||
"schritte": ["Leere Wanne: Leckerlis reinwerfen", "Wasser einlaufen lassen: immer mehr", "Kurzes Abduschen", "Abtrocknen mit Handtuch trainieren"],
|
||
"tipp": "Lauwarmes Wasser — nie heiß oder kalt."},
|
||
{"id": "body_halsband", "name": "Halsband / Geschirr anlegen","kat": "Körperpflege", "schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "3–5 Min",
|
||
"beschreibung": "Hund duldet Halsband und Geschirr ohne Stress.",
|
||
"schritte": ["Halsband zeigen: Leckerli daneben", "Kurz anlegen → sofort abnehmen + belohnen", "Länger anlassen", "Geschirr: Kopf selbst reinstecken trainieren"],
|
||
"tipp": "Nie über Kopf werfen — von unten anlegen."},
|
||
{"id": "body_krallen", "name": "Krallen schneiden", "kat": "Körperpflege", "schwierigkeit": "Mittel", "alter": "Ab 8 Wo", "dauer": "5–10 Min",
|
||
"beschreibung": "Hund lässt Krallen schneiden — ohne Kampf und Schreien.",
|
||
"schritte": ["Pfoten täglich anfassen", "Krallenschneider zeigen: Leckerli", "Eine Kralle: schneiden → Leckerli", "Nicht zu kurz schneiden (Blutgefäß)"],
|
||
"tipp": "Kratzbretter trainieren: Hund schleift selbst ab!"},
|
||
{"id": "body_check", "name": "Körpercheck / Ganzkörperuntersuchung","kat":"Körperpflege","schwierigkeit":"Anfänger","alter":"Ab 8 Wo","dauer":"5 Min täglich",
|
||
"beschreibung": "Täglicher Ganzkörper-Check — Zecken, Beulen, Verletzungen früh entdecken.",
|
||
"schritte": ["Täglich von Nase bis Schwanz abtasten", "Pfoten zwischen Zehen prüfen", "Hund steht ruhig → belohnen", "Auffälligkeiten notieren"],
|
||
"tipp": "Verhindert teure Tierarztbesuche — Früherkennung ist alles."},
|
||
# ── ERWEITERTE HUNDESPORT ────────────────────────────────────────
|
||
{"id": "sport_canicross", "name": "Canicross / Jogging", "kat": "Hundesport", "schwierigkeit": "Anfänger", "alter": "Ab 1 Jahr", "dauer": "30–60 Min",
|
||
"beschreibung": "Hund und Mensch laufen zusammen — der natürlichste Sport!",
|
||
"schritte": ["Leinenführigkeit zuerst", "Spezialgeschirr für Canicross", "Langsam: 10 Min → schrittweise mehr", "Links/Rechts-Kommandos für Richtung"],
|
||
"tipp": "Erst ab 12 Monaten — Skelett vorher nicht voll ausgewachsen."},
|
||
{"id": "sport_schwimmen", "name": "Schwimmen lernen", "kat": "Hundesport", "schwierigkeit": "Anfänger", "alter": "Ab 6 Mo", "dauer": "30 Min",
|
||
"beschreibung": "Hund lernt schwimmen — tolle Auslastung und gelenkschonend.",
|
||
"schritte": ["Seichtes Wasser: Pfoten nass machen", "Leckerlis ins Wasser werfen", "Spielzeug ins flache Wasser", "Schwimmweste für unsichere Hunde"],
|
||
"tipp": "Nicht alle Hunde können instinktiv schwimmen — Kurzmaulrassen aufpassen!"},
|
||
{"id": "sport_rally", "name": "Rally Obedience Einführung", "kat": "Hundesport", "schwierigkeit": "Mittel", "alter": "Ab 1 Jahr", "dauer": "20–30 Min",
|
||
"beschreibung": "Gemeinsam durch Parcours mit Schildern — Sport für Kopf und Körper.",
|
||
"schritte": ["Grundkommandos sehr sicher", "Erste Schilder: Sitz, Platz, Hier", "Kleinen Parcours bauen", "Flüssig von Schild zu Schild"],
|
||
"tipp": "Einstieg in Hundesport ohne Agility-Equipment möglich."},
|
||
{"id": "sport_flyball_basic", "name": "Flyball Grundlagen", "kat": "Hundesport", "schwierigkeit": "Fortg.", "alter": "Ab 1 Jahr", "dauer": "20–30 Min",
|
||
"beschreibung": "Hund springt Hindernisse, drückt Pedal, fängt Ball — Teamsport!",
|
||
"schritte": ["Ball-Motivation sehr hoch halten", "Pedal drücken trainieren", "Hürden einzeln", "Alles kombinieren in Sequenz"],
|
||
"tipp": "Wird in Teams gespielt — lokale Flyball-Vereine suchen!"},
|
||
{"id": "sport_gewicht", "name": "Weight Pulling Basics", "kat": "Hundesport", "schwierigkeit": "Mittel", "alter": "Ab 2 Jahre","dauer": "15–30 Min",
|
||
"beschreibung": "Hund zieht Gewichte auf Kommando — Kraft und Fokus trainieren.",
|
||
"schritte": ["Geschirr trainieren", "Leichtes Objekt ziehen: Karton", "Mehr Gewicht schrittweise", "Kommando 'Zieh' einführen"],
|
||
"tipp": "Erst ab 2 Jahren — Wirbelsäule vorher nicht belastungsreif."},
|
||
# ── WELPEN-SPEZIAL ────────────────────────────────────────────────
|
||
{"id": "welpe_name", "name": "Namen lernen (Welpe)", "kat": "Welpe Basics", "schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "5 Min täglich",
|
||
"beschreibung": "Welpe reagiert zuverlässig auf seinen Namen — Basis für alles.",
|
||
"schritte": ["Namen sagen wenn Welpe schaut", "Markerwort + Leckerli", "Namen in vielen Situationen üben", "Nie beim Schimpfen den Namen verwenden"],
|
||
"tipp": "Namen = immer positiv. Nie als Strafe."},
|
||
{"id": "welpe_stubenrein","name": "Stubenreinheit", "kat": "Welpe Basics", "schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "Wochenlang",
|
||
"beschreibung": "Welpe macht Geschäfte nur draußen — ohne Unfälle in der Wohnung.",
|
||
"schritte": ["Alle 1–2 Stunden raus", "Nach Schlafen und Fressen sofort raus", "Draußen loben bei Erfolg", "Unfälle innen nie bestrafen — einfach wegmachen"],
|
||
"tipp": "Welpen können bis 4–6 Monate die Blase noch nicht lange halten."},
|
||
{"id": "welpe_transport", "name": "Transportbox (Welpe)", "kat": "Welpe Basics", "schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "Täglich",
|
||
"beschreibung": "Welpe liebt seine Box als sicheren Rückzugsort.",
|
||
"schritte": ["Box offen lassen, Leckerlis reinwerfen", "Welpe geht freiwillig rein", "Tür für kurze Zeit schließen", "Als Schlafplatz etablieren"],
|
||
"tipp": "Box nie als Strafe — sicherer Hafen, nicht Gefängnis."},
|
||
{"id": "welpe_bindung", "name": "Bindung aufbauen", "kat": "Welpe Basics", "schwierigkeit": "Anfänger", "alter": "Ab 8 Wo", "dauer": "Täglich",
|
||
"beschreibung": "Intensive positive Bindung zwischen Hund und Mensch — Basis für Training.",
|
||
"schritte": ["Täglich 10 Min gezielt spielen", "Augenkontakt belohnen", "Sanftes Handling täglich", "Hund beobachten — was liebt er?"],
|
||
"tipp": "Bindung ist wichtiger als jede Übung — investiere hier!"},
|
||
{"id": "welpe_sozial", "name": "Sozialisierung Tiere", "kat": "Welpe Basics", "schwierigkeit": "Anfänger", "alter": "8–16 Wochen","dauer": "Täglich",
|
||
"beschreibung": "Welpe kennt Katzen, andere Hunde, Vögel — bleibt ruhig dabei.",
|
||
"schritte": ["Kontrollierte, positive Begegnungen", "Nie überfordern", "Körpersprache des Welpen lesen", "Positive Assoziation aufbauen"],
|
||
"tipp": "Jede neue Begegnung in dieser Phase prägt lebenslang."},
|
||
]
|
||
|
||
# ------------------------------------------------------------------
|
||
# Pflege-Tipps-Bibliothek
|
||
# ------------------------------------------------------------------
|
||
_PFLEGE_TIPPS = [
|
||
# ── FELL ────────────────────────────────────────────────────────
|
||
{"id":"fell_buersten_kurz","titel":"Bürsten bei Kurzhaarfell","kat":"Fell",
|
||
"beschreibung":"Kurzhaar braucht weniger, aber regelmäßige Pflege — für Glanz und Gesundheit.",
|
||
"schritte":["Gummibürste oder Hundekamm","Gegen und mit dem Fell bürsten","Totreste entfernen","Glanzspray optional"],
|
||
"materialien":"Gummibürste, feiner Kamm, Mikrofasertuch","haeufigkeit":"1–2x pro Woche",
|
||
"fell_typ":"kurz","saison":None,"tipp":"Kurzhaar verliert trotzdem Haare — wöchentliches Bürsten hält Wohnung sauber."},
|
||
{"id":"fell_buersten_lang","titel":"Bürsten bei Langhaarfell","kat":"Fell",
|
||
"beschreibung":"Langhaar verfilzt schnell — tägliches Bürsten verhindert schmerzhafte Knoten.",
|
||
"schritte":["Abschnittweise arbeiten","Zuerst Unterwolle mit Undercoat-Rake","Dann Bürste über alles","Verfilzungen mit Entfilzer-Spray lösen, nie reißen"],
|
||
"materialien":"Undercoat-Rake, Slicker-Bürste, Entfilzer-Spray","haeufigkeit":"Täglich",
|
||
"fell_typ":"lang","saison":None,"tipp":"Von Beinen und Schwanz beginnen — dort filzt es zuerst."},
|
||
{"id":"fell_buersten_lockig","titel":"Pflege bei Lockenfell (Pudel, Labradoodle)","kat":"Fell",
|
||
"beschreibung":"Lockiges Fell verliert kaum Haare, verfilzt aber stark — spezielle Technik nötig.",
|
||
"schritte":["Täglich durchkämmen mit Metallkamm","Verfilzungen mit Finger lösen, dann Kamm","Alle 6–8 Wochen Schertermin","Zwischen Augen und Pfoten regelmäßig kürzen"],
|
||
"materialien":"Metallkamm, Pin-Bürste, Schere","haeufigkeit":"Täglich + 6–8 Wochen Schertermin",
|
||
"fell_typ":"lockig","saison":None,"fell_pflege_art":"schneiden","tipp":"Lockiges Fell = hypoallergen, aber Pflegeaufwand unterschätzt!"},
|
||
{"id":"fell_scheren_technik","titel":"Fell schneiden: Technik & Scheren-Tipps","kat":"Fell",
|
||
"beschreibung":"Für Rassen mit kontinuierlichem Fellwuchs (Pudel, Bichon, Spoodle) — Scheren statt Trimmen!",
|
||
"schritte":["Fell nach dem Bad komplett trocknen und bürsten","Schere oder Clipper parallel zur Haarwuchsrichtung führen","Empfindliche Stellen (Gesicht, Pfoten) mit Effilierschere","Alle 6–8 Wochen zum Groomer oder selbst lernen","Nach dem Scheren: Fell bürsten und kontrollieren"],
|
||
"materialien":"Effilierschere, Clipper, Metallkamm","haeufigkeit":"Alle 6–8 Wochen",
|
||
"fell_typ":"lockig","saison":None,"fell_pflege_art":"schneiden","tipp":"Nie nasses Fell scheren — immer erst trocknen, sonst ungleichmäßiges Ergebnis."},
|
||
{"id":"fell_trimmen_technik","titel":"Fell trimmen: Stripping beim Rauhaar-Terrier","kat":"Fell",
|
||
"beschreibung":"Rauhaardrahtiges Fell (Westie, Schnauzer, Jack Russell) hat natürliche Wachstumsbegrenzung — Trimmen statt Scheren!",
|
||
"schritte":["Daumen und Zeigefinger: abgestorbene Haare zupfen (Stripping)","Immer in Haarwuchsrichtung arbeiten","Unterwolle mit feinem Rake ausbürsten","Niemals scheren — zerstört Textur dauerhaft","Alle 3–4 Monate professionelles Hand-Stripping"],
|
||
"materialien":"Stripper-Messer (Trimmmesser), Rake, Kreide für Grip","haeufigkeit":"Alle 3–4 Monate",
|
||
"fell_typ":"alle","saison":None,"fell_pflege_art":"trimmen","tipp":"Geschorenes Drahthaarfell verliert seine typische Textur dauerhaft — immer trimmen!"},
|
||
{"id":"fell_unterwolle", "titel":"Unterwolle ausbürsten (Fellwechsel)","kat":"Fell",
|
||
"beschreibung":"Zweimal jährlich toter Unterwolle-Berg — richtig ausgebürstet statt überall verteilt.",
|
||
"schritte":["Undercoat-Rake gegen Haarwuchsrichtung","Abschnittweise: Rücken, Flanken, Bauch","Furminator maximal 2x/Woche","Nach dem Bürsten: Hund ausschütteln lassen"],
|
||
"materialien":"Furminator oder Undercoat-Rake","haeufigkeit":"Täglich während Fellwechsel",
|
||
"fell_typ":"doppel","saison":"fruehling,herbst","tipp":"Einen Schuh voll Haare pro Session ist normal bei Huskies & Co."},
|
||
{"id":"fell_bad_timing", "titel":"Wann und wie oft Baden?","kat":"Fell",
|
||
"beschreibung":"Zu häufiges Baden zerstört den natürlichen Fellschutz — die richtige Balance.",
|
||
"schritte":["Kurzhaar: alle 6–8 Wochen","Langhaar/Lockig: alle 4–6 Wochen","Vor dem Bad: komplett ausbürsten","Nur Hundeshampoo verwenden — Menschen-Shampoo macht Fell spröde"],
|
||
"materialien":"Hundeshampoo, Handbrause, große Handtücher","haeufigkeit":"4–8 Wochen",
|
||
"fell_typ":"alle","saison":None,"tipp":"Fön auf niedrigster Stufe — hohe Hitze schadet dem Fell."},
|
||
{"id":"fell_trocknen", "titel":"Richtiges Trocknen nach dem Bad","kat":"Fell",
|
||
"beschreibung":"Nasses Fell eingerollt schläft = Hautprobleme — so trocknet man richtig.",
|
||
"schritte":["Handtuch: klopfen, nicht reiben", "Hundeföhn oder normaler Fön auf Kalt/Niedrig","Fell gleichzeitig bürsten","Niemals nass in kalte Luft"],
|
||
"materialien":"Absorbier-Handtuch, Hundeföhn","haeufigkeit":"Nach jedem Bad",
|
||
"fell_typ":"alle","saison":None,"tipp":"Mikrofaser-Handtücher nehmen 5x mehr Wasser auf als normale."},
|
||
{"id":"fell_geruch", "titel":"Fell-Geruch zwischen Bädern","kat":"Fell",
|
||
"beschreibung":"Hund riecht ohne Baden — Trockenshampoo und Hausmittel helfen.",
|
||
"schritte":["Trockenshampoo einsprühen","3 Min einwirken lassen","Ausbürsten","Alternative: Backpulver einmassieren, ausklopfen"],
|
||
"materialien":"Trockenshampoo für Hunde, Bürste","haeufigkeit":"Nach Bedarf",
|
||
"fell_typ":"alle","saison":None,"tipp":"Geruch oft durch Unterkieferdrüsen — dort bürsten!"},
|
||
{"id":"fell_ernaehrung", "titel":"Ernährung für gesundes, glänzendes Fell","kat":"Fell",
|
||
"beschreibung":"Schönes Fell kommt von innen — die richtigen Nährstoffe machen den Unterschied.",
|
||
"schritte":["Lachsöl täglich: 1 TL pro 10 kg","Omega-3-Fettsäuren im Futter","Hochwertige Proteine","Biotin-Supplement bei stumpfem Fell"],
|
||
"materialien":"Lachsöl, hochwertiges Futter","haeufigkeit":"Täglich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Verbesserung sichtbar nach 4–8 Wochen."},
|
||
# ── KRALLEN ─────────────────────────────────────────────────────
|
||
{"id":"krallen_schneiden", "titel":"Krallen richtig schneiden","kat":"Krallen",
|
||
"beschreibung":"Zu lange Krallen verändern die Körperhaltung und können schmerzhaft sein.",
|
||
"schritte":["Ruhige Umgebung, Hund entspannt","Krallenschneider scharf und sauber","Weißes Fell: rosa Bereich (Quick) sehen → 2mm davor schneiden","Schwarze Krallen: kleine Scheiben bis weiß/grau sichtbar"],
|
||
"materialien":"Krallenschneider oder -feile, Blutungspulver für Notfall","haeufigkeit":"Alle 3–4 Wochen",
|
||
"fell_typ":"alle","saison":None,"tipp":"Schneidet man zu kurz: Blutung sofort mit Stärke oder Kaffeepulver stoppen."},
|
||
{"id":"krallen_schleifen", "titel":"Krallen-Schleifbrett trainieren","kat":"Krallen",
|
||
"beschreibung":"Hund schleift Krallen selbst ab — nie wieder Krallen-Kampf!",
|
||
"schritte":["Schleifbrett (Sandpapier auf Brett) vorlegen","Pfote draufstellen belohnen","Kratzbewegung formen","Hund macht es selbst auf Kommando"],
|
||
"materialien":"Kratzbrett selbstgebaut: Holz + grobes Sandpapier","haeufigkeit":"Wöchentlich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Hinterkrallen schleifen sich beim Laufen oft selbst ab."},
|
||
{"id":"krallen_dew", "titel":"Daumenkralle / Wolfskralle","kat":"Krallen",
|
||
"beschreibung":"Die Wolfskralle berührt keinen Boden — wächst ins Fleisch wenn vergessen!",
|
||
"schritte":["Wolfskralle an Innenseite des Vorderbeins suchen","Einige Hunde haben sie auch hinten","Häufiger schneiden als andere Krallen","Auf Einwachsung achten"],
|
||
"materialien":"Krallenschneider","haeufigkeit":"Alle 2–3 Wochen",
|
||
"fell_typ":"alle","saison":None,"tipp":"Wolfskralle ist DER häufigste Pflegefehler — vergiss sie nie!"},
|
||
# ── ZÄHNE ───────────────────────────────────────────────────────
|
||
{"id":"zahne_putzen", "titel":"Zähne täglich putzen","kat":"Zähne",
|
||
"beschreibung":"Zahnfleischentzündung betrifft 80% aller Hunde ab 3 Jahren — täglich putzen hilft.",
|
||
"schritte":["Hundezahnpasta auftragen (nie Menschenzahnpasta!)", "Finger einführen → kreisende Bewegung","Schrittweise zur Zahnbürste wechseln","Außenflächen priorisieren"],
|
||
"materialien":"Hundezahnpasta (Hühnergeschmack!), Fingerling oder Zahnbürste","haeufigkeit":"Täglich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Menschenzahnpasta ist giftig für Hunde — immer Hundezahnpasta!"},
|
||
{"id":"zahne_kauen", "titel":"Kauartikel für gesunde Zähne","kat":"Zähne",
|
||
"beschreibung":"Kauen reinigt Zähne mechanisch — das natürlichste Zahnpflegeprogramm.",
|
||
"schritte":["Roher Rinderknochen (nur roh!)", "Kauknochen aus Rinderhaut", "Dental-Chews (Greenies, Milkbone)", "Kauspielzeug aus Nylon"],
|
||
"materialien":"Kauknochen, Dental Chews","haeufigkeit":"Täglich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Gekochte Knochen splittern und sind lebensgefährlich — nur ROH!"},
|
||
{"id":"zahne_zahnstein", "titel":"Zahnstein erkennen & vorbeugen","kat":"Zähne",
|
||
"beschreibung":"Gelb-braune Beläge = Zahnstein → nur Tierarzt oder Ultraschall entfernt das.",
|
||
"schritte":["Regelmäßig Zähne kontrollieren (Lippen hochziehen)","Gelb = Plaque (wegputzbar), Braun = Zahnstein (Tierarzt)","Zahnpflege-Diät-Futter verwenden","Tierarzt-Kontrolle 1x/Jahr"],
|
||
"materialien":"Zahnpflegefutter, Zahngel","haeufigkeit":"Kontrolle wöchentlich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Zahnsteinentfernung in Narkose kostet 300–800€ — Vorsorge lohnt sich!"},
|
||
# ── OHREN ───────────────────────────────────────────────────────
|
||
{"id":"ohren_reinigen", "titel":"Ohren sauber halten","kat":"Ohren",
|
||
"beschreibung":"Dreckige Ohren = Ohrenentzündung — regelmäßige Reinigung schützt.",
|
||
"schritte":["Äußeren Gehörgang begutachten","Ohrenpflege-Lösung einträufeln","Mit weichem Tuch/Wattebausch reinigen","Nie mit Wattestäbchen tief rein"],
|
||
"materialien":"Ohrenreiniger für Hunde, Wattebausch","haeufigkeit":"Alle 2–4 Wochen",
|
||
"fell_typ":"alle","saison":None,"tipp":"Übler Geruch = Entzündung → sofort zum Tierarzt!"},
|
||
{"id":"ohren_haare", "titel":"Ohrenbehaarung trimmen","kat":"Ohren",
|
||
"beschreibung":"Dichte Ohrenbehaarung staut Luft — Otitis vorprogrammiert bei Hängeohrrassen.",
|
||
"schritte":["Haare im Außenohr vorsichtig mit Schere trimmen","Haare im inneren Ohrkanal: Trimmer oder Zupfen (nur wenn gelernt)", "Nach Schwimmen: Ohren immer trocknen","Beim Groomer: Ohren entnehmen lassen"],
|
||
"materialien":"Schere, Trimmer, Ohrenpfleger","haeufigkeit":"Monatlich",
|
||
"fell_typ":"alle","saison":"sommer","rassengruppe":"Hängeohrrassen","tipp":"Hängeohren (Cocker, Beagle) = höheres Risiko → häufiger kontrollieren."},
|
||
{"id":"ohren_check", "titel":"Wöchentlicher Ohren-Check","kat":"Ohren",
|
||
"beschreibung":"Früherkennung von Entzündungen spart Schmerzen und Kosten.",
|
||
"schritte":["Ohren hochklappen und reinschauen","Normal: hellrosa, leicht glänzend, kaum Geruch","Alarm: rot, geschwollen, dunkler Belag, Kratzen","Bei Auffälligkeiten: Tierarzt binnen 24h"],
|
||
"materialien":"Taschenlicht","haeufigkeit":"Wöchentlich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Hunde mit Allergien haben häufiger Ohrprobleme."},
|
||
# ── AUGEN ───────────────────────────────────────────────────────
|
||
{"id":"augen_reinigen", "titel":"Augenränder reinigen","kat":"Augen",
|
||
"beschreibung":"Tränenflecken und Schlieren — mit der richtigen Technik verschwinden sie.",
|
||
"schritte":["Feuchtes weiches Tuch oder Wattebausch","Von innen nach außen wischen","Nie auf die Hornhaut drücken","Augenreiniger für Hunde optional"],
|
||
"materialien":"Augenreiniger, Wattepads","haeufigkeit":"Täglich oder bei Bedarf",
|
||
"fell_typ":"alle","saison":None,"tipp":"Braune Tränenflecken bei Weißfell: spezielle Produkte oder Tierarzt."},
|
||
{"id":"augen_fell", "titel":"Fell vor Augen schneiden","kat":"Augen",
|
||
"beschreibung":"Fell im Gesicht kann Hornhaut kratzen — regelmäßiges Trimmen schützt.",
|
||
"schritte":["Abgerundete Schere verwenden","Fell von Augenrändern wegschneiden","Nie beim unruhigen Hund schneiden","Professioneller Groomer alle 4–6 Wochen"],
|
||
"materialien":"Abgerundete Schere","haeufigkeit":"Monatlich",
|
||
"fell_typ":"lang,lockig","saison":None,"rassengruppe":"Langhaarrassen","tipp":"Niemals spitze Schere in Augennähe — immer stumpf/abgerundet!"},
|
||
{"id":"augen_check", "titel":"Täglicher Augen-Check","kat":"Augen",
|
||
"beschreibung":"Augen erzählen viel über die Gesundheit — täglich kurz hinschauen.",
|
||
"schritte":["Klar und glänzend = gut","Trüb, gerötet, Ausfluss = Problem","Blinzeln oder Kratzen = Tierarzt","Licht mal mit Taschenlampe auf Pupillen"],
|
||
"materialien":None,"haeufigkeit":"Täglich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Plötzliche Trübung = Tierarzt HEUTE."},
|
||
# ── PFOTEN ──────────────────────────────────────────────────────
|
||
{"id":"pfoten_sommer", "titel":"Pfoten im Sommer schützen","kat":"Pfoten",
|
||
"beschreibung":"Asphalt im Sommer bis 70°C heiß — Pfotenballen verbrennen in Sekunden.",
|
||
"schritte":["Handtest: Hand 5 Sek auf Asphalt → zu heiß wenn unerträglich","Morgens/abends spazieren","Pfotenbalsam schützt und pflegt","Hundeschuhe für empfindliche Hunde"],
|
||
"materialien":"Pfotenbalsam, Hundeschuhe","haeufigkeit":"Täglich im Sommer",
|
||
"fell_typ":"alle","saison":"sommer","tipp":"Regel: Wenn du barfuß nicht draufstehen kannst, dein Hund auch nicht!"},
|
||
{"id":"pfoten_winter", "titel":"Pfoten im Winter / Streusalz","kat":"Pfoten",
|
||
"beschreibung":"Streusalz ist ätzend — rissige Ballen und Vergiftung durch Ablecken.",
|
||
"schritte":["Vor dem Spaziergang: Pfotenbalsam auftragen","Nach dem Spaziergang: Pfoten mit lauwarmem Wasser abspülen","Zwischen Zehen prüfen: kein Eis","Hundeschuhe bei viel Salz"],
|
||
"materialien":"Pfotenbalsam, Wasser, kleines Becken","haeufigkeit":"Täglich im Winter",
|
||
"fell_typ":"alle","saison":"winter","tipp":"Musher's Secret ist der Pfoten-Klassiker für den Winter."},
|
||
{"id":"pfoten_haare", "titel":"Pfotenhaare trimmen","kat":"Pfoten",
|
||
"beschreibung":"Zu lange Haare zwischen den Zehen = Rutschgefahr und Schmutzfänger.",
|
||
"schritte":["Haare zwischen Zehenballen mit Schere oder Trimmer","Bündig mit Ballenoberfläche kürzen","Nicht zu kurz — Schutzfunktion erhalten","Auf Wunden und Einschüsse achten"],
|
||
"materialien":"Abgerundete Schere oder Trimmer","haeufigkeit":"Alle 3–4 Wochen",
|
||
"fell_typ":"lang,lockig","saison":None,"tipp":"Rutschiger Parkett + lange Pfotenhaare = Kreuzbandriss-Risiko!"},
|
||
{"id":"pfoten_ballen", "titel":"Pfotenballen pflegen","kat":"Pfoten",
|
||
"beschreibung":"Rissige Ballen sind schmerzhaft — regelmäßige Pflege beugt vor.",
|
||
"schritte":["Ballen wöchentlich kontrollieren","Risse eincremen mit Pfotenbalsam","Tiefe Risse: Verbandsmull + Tierarzt","Im Sommer nach Sand/Strand extra eincremen"],
|
||
"materialien":"Pfotenbalsam, Wattepads","haeufigkeit":"Wöchentlich (täglich im Winter/Sommer)",
|
||
"fell_typ":"alle","saison":None,"tipp":"Bienen-Propolis-Balsam hat heilende Wirkung auf rissige Ballen."},
|
||
{"id":"pfoten_reinigen", "titel":"Pfoten nach dem Spaziergang reinigen","kat":"Pfoten",
|
||
"beschreibung":"Dreck, Giftstoffe und Parasiten kommen mit rein — kurze Routine macht Unterschied.",
|
||
"schritte":["Kleines Becken mit lauwarmem Wasser","Jede Pfote kurz eintauchen und abwischen","Zwischen Zehen kontrollieren","Abtrocknen — feuchte Pfoten = Pilzgefahr"],
|
||
"materialien":"Pfoten-Reinigungsbecher, Handtuch","haeufigkeit":"Nach jedem Spaziergang",
|
||
"fell_typ":"alle","saison":None,"tipp":"Pfotenwasch-Becher (Dexas Mudbuster) ist der Gamechanger!"},
|
||
# ── PARASITEN ───────────────────────────────────────────────────
|
||
{"id":"parasiten_zecken_check","titel":"Zecken-Check nach Spaziergang","kat":"Parasiten",
|
||
"beschreibung":"Zecken übertragen Borreliose und FSME — täglicher Check rettet Leben.",
|
||
"schritte":["Kopf: um Augen, Ohren, Hals","Achseln und Leiste","Zwischen Zehen","Schwanzwurzel und Analbereich","Fell gegen Strich durchkämmen"],
|
||
"materialien":"Feine Zeckenzange oder Zeckenhaken","haeufigkeit":"Nach jedem Spaziergang im Gras",
|
||
"fell_typ":"alle","saison":"fruehling,sommer,herbst","tipp":"Zecken erst nach 24h übertragen meist Erreger — täglich reicht!"},
|
||
{"id":"parasiten_zecken_entfernen","titel":"Zecke richtig entfernen","kat":"Parasiten",
|
||
"beschreibung":"Falsch entfernt = Erkrankungsrisiko erhöht — diese Technik ist richtig.",
|
||
"schritte":["Zeckenzange nah an Haut ansetzen","Gerade rausziehen, nicht drehen oder quetschen","Kein Öl, Klebstoff oder Nagellack","Bissstelle 2 Wochen beobachten","Bei Rötung: Tierarzt"],
|
||
"materialien":"Zeckenzange/-haken","haeufigkeit":"Sofort wenn Zecke gefunden",
|
||
"fell_typ":"alle","saison":"fruehling,sommer,herbst","tipp":"Fotografiere Zecke und Bissstelle zur Dokumentation."},
|
||
{"id":"parasiten_prophylaxe","titel":"Zecken- und Floh-Prophylaxe","kat":"Parasiten",
|
||
"beschreibung":"Spot-on, Tablette oder Halsband — die Vor- und Nachteile.",
|
||
"schritte":["Spot-on: monatlich auf Nacken, zuverlässig","Tabletten (NexGard, Bravecto): bequem, für Wasserhunde","Zeckenhalsband (Seresto): 8 Monate Schutz","Natürlich: Kokosöl, Schwarzkümmelöl (schwächerer Schutz)"],
|
||
"materialien":"Tierarzt-Empfehlung","haeufigkeit":"Je nach Produkt",
|
||
"fell_typ":"alle","saison":"fruehling,sommer,herbst","tipp":"Produkte immer vom Tierarzt — manche Katzen-Präparate töten Hunde!"},
|
||
{"id":"parasiten_floh", "titel":"Floh-Befall erkennen und behandeln","kat":"Parasiten",
|
||
"beschreibung":"Flöhe sieht man selten — aber Flohkot ist eindeutig.",
|
||
"schritte":["Weißes Tuch unter Hund, kämmen → schwarze Punkte = Flohkot","Feuchtes Küchentuch: Flohkot wird rötlich-braun","Hund + Wohnung gleichzeitig behandeln","Flohpuder und -spray für Umgebung"],
|
||
"materialien":"Floh-Kamm, Flohspray, Tierarzt-Präparat","haeufigkeit":"Bei Verdacht + Prophylaxe",
|
||
"fell_typ":"alle","saison":"sommer","tipp":"80% der Flöhe leben in der Wohnung, nicht am Hund!"},
|
||
{"id":"parasiten_wurm", "titel":"Wurm-Prophylaxe","kat":"Parasiten",
|
||
"beschreibung":"Würmer übertragen sich auf Menschen — regelmäßige Behandlung schützt die Familie.",
|
||
"schritte":["Wurmkur alle 3–6 Monate (je nach Lebensweise)","Kot-Untersuchung beim Tierarzt","Nach Zeckenbiss: auf Herzwurm testen (Reisehunde)","Hände waschen nach Hundekontakt"],
|
||
"materialien":"Wurmmittel vom Tierarzt","haeufigkeit":"Alle 3–6 Monate",
|
||
"fell_typ":"alle","saison":None,"tipp":"Rohfleisch-Hunde öfter entwurmen — rohes Fleisch überträgt Parasiten."},
|
||
# ── SAISONAL ────────────────────────────────────────────────────
|
||
{"id":"saison_hitze", "titel":"Pflege im Sommer / Hitzeschutz","kat":"Saisonal",
|
||
"beschreibung":"Hunde schwitzen durch Pfoten und Hecheln — Überhitzung ist lebensgefährlich.",
|
||
"schritte":["Spaziergänge in früh/spät","Immer Wasser dabei","Nasses Tuch auf Bauch kühlt schnell","Niemals im Auto lassen"],
|
||
"materialien":"Kühlmatte, Kühlweste, Wassernapf","haeufigkeit":"Täglich im Sommer",
|
||
"fell_typ":"alle","saison":"sommer","tipp":"Kurznasige Rassen (Mops, Bulldogge): extreme Hitzegefahr!"},
|
||
{"id":"saison_scheren", "titel":"Sommerhaarschnitt — ja oder nein?","kat":"Saisonal",
|
||
"beschreibung":"Doppelfell scheren schadet mehr als es nützt — Missverständnis aufklären!",
|
||
"schritte":["Doppelfell (Husky, Retriever): NICHT scheren — kühlt selbst","Einfaches langes Fell: kürzen ok","Stattdessen: intensiv ausbürsten, Unterwolle entfernen","Scheren nur wenn verfilzt oder Veterinär rät"],
|
||
"materialien":"Bürste, Undercoat-Rake","haeufigkeit":"Saisonal",
|
||
"fell_typ":"doppel,lang","saison":"sommer","tipp":"Geschorenes Doppelfell kann sich falsch nachwachsen — Textur-Veränderung dauerhaft."},
|
||
{"id":"saison_winter_kalt","titel":"Winterpflege für empfindliche Hunde","kat":"Saisonal",
|
||
"beschreibung":"Kleine, kurzhaarige und ältere Hunde frieren — Schutz ist Pflicht.",
|
||
"schritte":["Hundemantel für Kurzhaar unter 5°C","Nach draußen: nie nass raus","Fell nach Schnee/Regen sofort trocknen","Alters-Check: Gelenke im Winter öfter warm halten"],
|
||
"materialien":"Hundemantel, Pfotenschutz","haeufigkeit":"Täglich im Winter",
|
||
"fell_typ":"kurz","saison":"winter","rassengruppe":"Kurzhaarrassen","tipp":"Chihuahua und Co. im Winter wirklich mit Mantel — kein Modethema!"},
|
||
{"id":"saison_pollen", "titel":"Allergiezeit / Pollenflug","kat":"Saisonal",
|
||
"beschreibung":"Hunde haben Pollenallergien — Jucken, Pfotenlecken, Ohrenprobleme.",
|
||
"schritte":["Nach Spaziergang Fell und Pfoten abwischen","Pollen-App nutzen: Hochphasen meiden","Tierarzt: Antihistaminika für Hunde","Hypoallergenes Shampoo verwenden"],
|
||
"materialien":"Pollen-App, feuchte Tücher","haeufigkeit":"Täglich im Frühling",
|
||
"fell_typ":"alle","saison":"fruehling","tipp":"Pollenallergie kann Hautprobleme verursachen — oft verwechselt mit Futtermittelallergie."},
|
||
{"id":"saison_fell_wechsel","titel":"Fellwechsel-Saison meistern","kat":"Saisonal",
|
||
"beschreibung":"2x jährlich Haare-Chaos — mit der richtigen Routine überleben alle.",
|
||
"schritte":["Täglich bürsten während Fellwechsel","Furminator 2x/Woche (nicht öfter)","Lüfter hilft lose Haare wegzublasen","Professionelles Grooming buchen"],
|
||
"materialien":"Furminator, Staubsauger mit Tierhaar-Aufsatz","haeufigkeit":"Täglich März–Mai, Sep–Nov",
|
||
"fell_typ":"doppel","saison":"fruehling,herbst","tipp":"Ein 10-Minuten-Bürstsession täglich ersetzt stundenlange Staubsauger-Session."},
|
||
# ── GESUNDHEITSVORSORGE ──────────────────────────────────────────
|
||
{"id":"gesund_impfung", "titel":"Impfkalender verstehen","kat":"Gesundheitsvorsorge",
|
||
"beschreibung":"Welche Impfungen wirklich nötig sind — Kern- vs. Non-Core-Impfungen.",
|
||
"schritte":["Kernimpfungen: Staupe, Parvo, Leptospirose jährlich/alle 3 Jahre","Non-Core je nach Lebensweise: Tollwut, Zwinger-Husten","Impfpass immer aktuell halten","Jährlicher Tierarztbesuch zum Check"],
|
||
"materialien":"Impfpass","haeufigkeit":"Jährlich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Überimpfung gibt es — Titer-Test statt Routine-Booster möglich."},
|
||
{"id":"gesund_vorsorge", "titel":"Jährlicher Gesundheitscheck","kat":"Gesundheitsvorsorge",
|
||
"beschreibung":"Blutbild + körperliche Untersuchung einmal jährlich — Früherkennung rettet Leben.",
|
||
"schritte":["Einmal jährlich Tierarzt auch ohne Anlass","Ab 7 Jahren: halbjährlich","Blutbild gibt Einblick in Organfunktion","Gewicht kontrollieren"],
|
||
"materialien":"Tierarzt-Termin","haeufigkeit":"Jährlich (ab 7 J. halbjährlich)",
|
||
"fell_typ":"alle","saison":None,"tipp":"Senior-Hunde ab 7 Jahre: halbjährlicher Check lohnt sich sehr."},
|
||
{"id":"gesund_gewicht", "titel":"Idealgewicht halten","kat":"Gesundheitsvorsorge",
|
||
"beschreibung":"70% aller Hunde in Deutschland sind übergewichtig — Rippen müssen fühlbar sein.",
|
||
"schritte":["Rippentest: leicht zu fühlen, nicht zu sehen → Idealgewicht","Taille von oben erkennbar","Leckerlis: max. 10% des Tagesbedarfs","Futtermenge nach Aktivität anpassen"],
|
||
"materialien":"Küchenwaage für Futter","haeufigkeit":"Wöchentliche Sichtkontrolle",
|
||
"fell_typ":"alle","saison":None,"tipp":"1 kg Übergewicht beim 5-kg-Hund = 20 kg beim Menschen!"},
|
||
{"id":"gesund_zahnkontrolle","titel":"Monatliche Mundhöhlen-Kontrolle","kat":"Gesundheitsvorsorge",
|
||
"beschreibung":"Zahn- und Zahnfleischprobleme schmerzen — und Hunde zeigen das selten.",
|
||
"schritte":["Lippen hochziehen: Zähne und Zahnfleisch sehen","Zahnfleisch: rosa, glatt = gut; rot, geschwollen = Problem","Zähne: weiß/crème = ok; braun = Zahnstein","Mundgeruch plötzlich stark = Tierarzt"],
|
||
"materialien":None,"haeufigkeit":"Monatlich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Hunde zeigen Zahnschmerzen durch schlechteres Fressen, weniger Spielen."},
|
||
{"id":"gesund_körpercheck","titel":"Wöchentlicher Körper-Check","kat":"Gesundheitsvorsorge",
|
||
"beschreibung":"5 Minuten wöchentlich — Beulen, Wunden, Veränderungen früh erkennen.",
|
||
"schritte":["Kopf bis Schwanz abtasten","Lymphknoten fühlen (Kieferwinkel, Hals, Achsel, Leiste)","Auf Schmerz-Reaktionen achten","Alles unbekannte: Tierarzt"],
|
||
"materialien":"Gutes Licht","haeufigkeit":"Wöchentlich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Du kennst deinen Hund am besten — vertrau deinem Gefühl."},
|
||
{"id":"gesund_senior", "titel":"Pflege für Senior-Hunde","kat":"Gesundheitsvorsorge",
|
||
"beschreibung":"Ab 7–8 Jahren ändern sich Bedürfnisse — angepasste Pflege macht Unterschied.",
|
||
"schritte":["Kürzere, öftere Spaziergänge statt langer Touren","Orthopädische Schlafmatte","Gelenk-Supplemente (Glucosamin, Omega-3)","Mehr Körperchecks, öftere Tierarztbesuche"],
|
||
"materialien":"Orthopädie-Schlafmatte, Gelenk-Supplements","haeufigkeit":"Täglich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Senior-Hund = mehr Pflege, mehr Liebe, mehr Tierarzt — so einfach."},
|
||
# ── WELPEN-PFLEGE ────────────────────────────────────────────────
|
||
{"id":"welpe_erste_pflege","titel":"Erste Pflege-Einführung beim Welpen","kat":"Welpen-Pflege",
|
||
"beschreibung":"Was du jetzt mit dem Welpen übst, macht das spätere Leben einfacher.",
|
||
"schritte":["Täglich Pfoten, Ohren, Mund anfassen — immer positiv", "Kämmen einführen mit weicher Bürste","Nagel-Feile zeigen: noch nicht benutzen, nur zeigen","Alles mit Leckerli verbinden"],
|
||
"materialien":"Weiche Bürste, Leckerlis","haeufigkeit":"Täglich",
|
||
"fell_typ":"alle","saison":None,"tipp":"Was du im ersten Jahr tust, sparst du im Rest des Lebens."},
|
||
{"id":"welpe_badegewöhnung","titel":"Welpe ans Baden gewöhnen","kat":"Welpen-Pflege",
|
||
"beschreibung":"Erste Baderr-Erfahrung prägt lebenslang — positiv starten!",
|
||
"schritte":["Leeres Becken mit Leckerlis auskleiden","Wasser einlaufen lassen: Welpe schaut zu","Pfoten einweichen lassen","Erstes Bad sehr kurz (2 Min), viel Lob"],
|
||
"materialien":"Flache Wanne, mildes Welpen-Shampoo","haeufigkeit":"Alle 6–8 Wochen",
|
||
"fell_typ":"alle","saison":None,"tipp":"Wassertemperatur: wie für ein Baby — lauwarm."},
|
||
]
|
||
|
||
_PROMPT_PFLEGE = '''\
|
||
Erstelle einen Social-Media-Post über diesen Hunde-Pflegetipp für Ban Yaro (banyaro.app).
|
||
|
||
Pflegetipp: {titel}
|
||
Kategorie: {kat}
|
||
Beschreibung: {beschreibung}
|
||
Schritte: {schritte}
|
||
Materialien: {materialien}
|
||
Häufigkeit: {haeufigkeit}
|
||
Profi-Tipp: {tipp}
|
||
{rasse_kontext}
|
||
|
||
Antworte NUR als JSON:
|
||
{{
|
||
"caption": "Post-Text mit Emojis, lehrreich aber locker, max 700 Zeichen. Startet mit einem Fakten-Hook.",
|
||
"hashtags": "10 Hashtags kommagetrennt: hundepflege, hundegesundheit, {kat_lower}, banyaro + passende",
|
||
"hook": "Erste Zeile die sofort Aufmerksamkeit fängt (Fakt oder Frage)",
|
||
"cta": "Frage ans Publikum passend zum Thema",
|
||
"visual_brief": "Was man fotografieren/filmen sollte: konkretes Bild das den Tipp zeigt",
|
||
"canva_notes": "Infografik-Idee: z.B. Schritt-für-Schritt mit Icons",
|
||
"unsplash_query": "2–3 englische Suchbegriffe für passendes Stockfoto",
|
||
"ai_score": <1-5>,
|
||
"category": "pflege",
|
||
"coaching": "Warum ist dieser Pflegetipp wichtig für die Zielgruppe? Wie macht man das als Video? (1–2 Sätze)"
|
||
}}
|
||
'''
|
||
|
||
_TRAINING_STILE = [
|
||
"tutorial", # "So geht das:"
|
||
"community", # "Sind grad dran das zu lernen"
|
||
"aspirational",# "Das könnte euer Hund auch"
|
||
]
|
||
|
||
_PROMPT_TRAINING = '''\
|
||
Erstelle einen Social-Media-Post über den Hundetraining-Tipp "{name}" für Ban Yaro (banyaro.app).
|
||
|
||
Kategorie: {kat}
|
||
Schwierigkeit: {schwierigkeit}
|
||
Alter: {alter}
|
||
Dauer: {dauer}
|
||
Beschreibung: {beschreibung}
|
||
Schritte: {schritte}
|
||
Profi-Tipp: {tipp}
|
||
|
||
Stil dieses Posts: "{stil}"
|
||
- "tutorial" → "Schaut, so geht das: [Schritte knapp erklärt]"
|
||
- "community" → "Sind grad dran das zu lernen 🙋 Wer kennt das?"
|
||
- "aspirational" → "Das könnte euer Hund auch — in nur [Dauer]!"
|
||
|
||
Antworte NUR als JSON:
|
||
{{
|
||
"caption": "Post-Text passend zum Stil, mit Emojis, max 600 Zeichen, lehrreich aber locker",
|
||
"hashtags": "10 Hashtags kommagetrennt ohne #: hundetraining, hundeschule, positivreinforcement, {name_lower}, banyaro + passende",
|
||
"hook": "Erste Zeile die sofort Aufmerksamkeit fängt",
|
||
"cta": "Frage ans Publikum: z.B. 'Übt ihr das auch gerade?' oder 'Zeigt uns eure Videos!'",
|
||
"visual_brief": "Was im Video/Foto zu sehen sein sollte: Person mit Hund beim Training, Leckerli sichtbar, bestimmte Körperhaltung",
|
||
"canva_notes": "Text-Overlay: Übungsname + 1 Key-Tipp als Grafik",
|
||
"unsplash_query": "dog training {name_en} positive reinforcement",
|
||
"ai_score": <1-5>,
|
||
"category": "training",
|
||
"coaching": "Tipp für die Creatorin: Warum spricht dieser Post die Zielgruppe an? Wie filmt man das am besten? (1-2 Sätze)"
|
||
}}
|
||
'''
|
||
|
||
logger = logging.getLogger(__name__)
|
||
router = APIRouter()
|
||
|
||
_PLATFORMS = {"tiktok", "instagram", "both"}
|
||
_FORMATS = {"reel", "story", "post", "carousel"}
|
||
_STATUSES = {"idea", "draft", "scheduled", "published", "archived"}
|
||
|
||
_SYSTEM = (
|
||
"Du bist Luna, der freundliche Social-Media-Coach von Ban Yaro (banyaro.app). "
|
||
"Du begleitest eine junge Content-Creatorin (15 Jahre) dabei, für die Hunde-App "
|
||
"auf TikTok und Instagram zu wachsen. "
|
||
"Erkläre immer kurz WARUM etwas funktioniert — sie soll lernen, nicht nur kopieren. "
|
||
"Ton: locker, ermutigend, auf Augenhöhe — nie von oben herab. "
|
||
"Zielgruppe der App: Hundebesitzer in DACH, 20–45 Jahre, Fokus Frauen. "
|
||
"Antworte immer auf Deutsch."
|
||
)
|
||
|
||
_PROMPT_SUGGESTIONS = '''\
|
||
Schlage 5 aktuelle, kreative Content-Ideen für Ban Yaro (Hunde-App) vor.
|
||
Heute: {datum} ({saison}).
|
||
Bereits verwendete Themen (nicht wiederholen):
|
||
{used_topics}
|
||
|
||
Antworte NUR als JSON-Array:
|
||
[
|
||
{{
|
||
"thema": "Konkretes Thema für einen Post (1 Satz)",
|
||
"warum": "Kurze Erklärung warum das gerade gut funktioniert (1-2 Sätze, als Coach)",
|
||
"format": "reel|post|story|carousel",
|
||
"platform": "tiktok|instagram|both",
|
||
"emoji": "1 passendes Emoji"
|
||
}}
|
||
]
|
||
Misch Formate und Plattformen. Aktuelle Trends, Saisonales und Evergreen-Themen kombinieren.
|
||
'''
|
||
|
||
_PROMPT_GENERATE = '''\
|
||
Erstelle einen vollständigen Social-Media-Content-Plan für folgenden Post.
|
||
|
||
Plattform: {platform}
|
||
Format: {format}
|
||
Thema: {topic}
|
||
{breed_info}
|
||
Bereits verwendete Themen — NICHT wiederholen, ähnliche Inhalte VERMEIDEN:
|
||
{used_topics}
|
||
|
||
Kategorie-Verteilung der letzten Posts (vermeide überfüllte Kategorien):
|
||
{category_stats}
|
||
|
||
Antworte NUR mit einem JSON-Objekt:
|
||
{{
|
||
"caption": "Post-Text (plattformgerecht, mit Emojis, max 2200 Zeichen für Instagram / 150 für TikTok)",
|
||
"hashtags": "kommagetrennte Hashtags ohne #, 5-15 Stück, mix aus groß/nische",
|
||
"hook": "Ersten 1-3 Sätze / Sekunden — sofortiger Aufmerksamkeitsfänger",
|
||
"cta": "Call-to-Action am Ende (Frage, Aufforderung zum Kommentieren/Teilen)",
|
||
"visual_brief": "Detaillierte Beschreibung was auf dem Foto/Video zu sehen sein soll (Motiv, Stimmung, Licht, Perspektive)",
|
||
"image_prompt": "Englischer DALL-E/Midjourney Prompt für ein passendes Illustration/Cartoon (wenn kein eigenes Foto verfügbar)",
|
||
"canva_notes": "Empfehlung für Canva: Template-Typ, Textüberlagerungen, Farbstimmung",
|
||
"script": {script_field},
|
||
"unsplash_query": "2-3 englische Suchbegriffe für Unsplash-Stockfotos",
|
||
"ai_score": <Zahl 1-5, geschätztes Engagement-Potenzial>,
|
||
"category": "Eine Kategorie aus: tipps|humor|rasse|community|gesundheit|training|saisonal|emotional|behind-the-scenes|quiz",
|
||
"coaching": "2-3 Sätze für die Creatorin: Warum dieser Hook? Warum diese Hashtags? Was macht diesen Post stark? Locker und ermutigend formuliert."
|
||
}}
|
||
'''
|
||
|
||
_SCRIPT_FIELD_REEL = '''"Videostruktur: Hook (0-3s): ... | Hauptteil (3-25s): Schritt 1... Schritt 2... | CTA (25-30s): ..."'''
|
||
_SCRIPT_FIELD_OTHER = "null"
|
||
|
||
_PROMPT_EVALUATE = '''\
|
||
Bewerte und verbessere diesen Social-Media-Entwurf für Ban Yaro (Hunde-App).
|
||
|
||
Plattform: {platform}
|
||
Format: {format}
|
||
Entwurf:
|
||
{draft}
|
||
|
||
Antworte NUR mit einem JSON-Objekt:
|
||
{{
|
||
"caption": "Verbesserter Post-Text",
|
||
"hashtags": "Optimierte Hashtags, kommagetrennt ohne #",
|
||
"hook": "Verbesserter Hook",
|
||
"cta": "Verbesserter CTA",
|
||
"visual_brief": "Empfehlung für passendes Bildmaterial",
|
||
"image_prompt": "DALL-E/Midjourney Prompt für Illustration",
|
||
"canva_notes": "Canva-Tipps",
|
||
"script": null,
|
||
"unsplash_query": "Unsplash-Suchbegriffe",
|
||
"ai_score": <1-5>,
|
||
"feedback": "Kurzes Feedback was gut war und was verbessert wurde (2-3 Sätze)"
|
||
}}
|
||
'''
|
||
|
||
|
||
class GenerateRequest(BaseModel):
|
||
platform: str = "both"
|
||
format: str = "post"
|
||
topic: str
|
||
breed_id: Optional[int] = None
|
||
|
||
|
||
class EvaluateRequest(BaseModel):
|
||
platform: str = "instagram"
|
||
format: str = "post"
|
||
draft: str
|
||
|
||
|
||
class StatusUpdate(BaseModel):
|
||
status: Optional[str] = None
|
||
scheduled_at: Optional[str] = None
|
||
published_at: Optional[str] = None
|
||
notes: Optional[str] = None
|
||
post_url: Optional[str] = None
|
||
|
||
|
||
def _used_topics(limit: int = 30) -> str:
|
||
with db() as conn:
|
||
rows = conn.execute(
|
||
"SELECT topic FROM social_content ORDER BY created_at DESC LIMIT ?",
|
||
(limit,),
|
||
).fetchall()
|
||
if not rows:
|
||
return "(noch keine)"
|
||
return "\n".join(f"- {r['topic']}" for r in rows)
|
||
|
||
|
||
def _category_stats(limit: int = 30) -> str:
|
||
with db() as conn:
|
||
rows = conn.execute(
|
||
"""SELECT category, COUNT(*) as n FROM social_content
|
||
WHERE category IS NOT NULL
|
||
ORDER BY created_at DESC LIMIT ?""",
|
||
(limit,),
|
||
).fetchall()
|
||
if not rows:
|
||
return "(noch keine Kategorien)"
|
||
from collections import Counter
|
||
counts = Counter(r["category"] for r in rows)
|
||
total = sum(counts.values())
|
||
lines = [f"- {cat}: {n}x ({round(n/total*100)}%)" for cat, n in counts.most_common()]
|
||
return "\n".join(lines)
|
||
|
||
|
||
def _diversity_warning(limit: int = 20) -> dict | None:
|
||
"""Gibt Warnung zurück wenn eine Kategorie >40% der letzten Posts dominiert."""
|
||
with db() as conn:
|
||
rows = conn.execute(
|
||
"""SELECT category FROM social_content
|
||
WHERE category IS NOT NULL
|
||
ORDER BY created_at DESC LIMIT ?""",
|
||
(limit,),
|
||
).fetchall()
|
||
if len(rows) < 5:
|
||
return None
|
||
from collections import Counter
|
||
counts = Counter(r["category"] for r in rows)
|
||
total = len(rows)
|
||
for cat, n in counts.most_common(1):
|
||
if n / total > 0.4:
|
||
_CAT_DE = {
|
||
"tipps": "Tipps", "humor": "Humor/Spaß", "rasse": "Rassen-Info",
|
||
"community": "Community", "gesundheit": "Gesundheit",
|
||
"training": "Training", "saisonal": "Saison-Content",
|
||
"emotional": "Emotionale Posts", "behind-the-scenes": "Behind the Scenes",
|
||
"quiz": "Quiz/Fragen",
|
||
}
|
||
missing = [c for c in _CAT_DE if c not in counts]
|
||
return {
|
||
"dominant": cat,
|
||
"dominant_de": _CAT_DE.get(cat, cat),
|
||
"pct": round(n / total * 100),
|
||
"suggestions": missing[:3],
|
||
"suggestions_de": [_CAT_DE.get(c, c) for c in missing[:3]],
|
||
}
|
||
return None
|
||
|
||
|
||
def _breed_info(breed_id: int | None) -> str:
|
||
if not breed_id:
|
||
return ""
|
||
with db() as conn:
|
||
r = conn.execute(
|
||
"SELECT name, beschreibung, temperament, groesse FROM wiki_rassen WHERE id=?",
|
||
(breed_id,),
|
||
).fetchone()
|
||
if not r:
|
||
return ""
|
||
parts = [f"Rasse: {r['name']}"]
|
||
if r["beschreibung"]:
|
||
parts.append(f"Info: {r['beschreibung'][:300]}")
|
||
if r["temperament"]:
|
||
parts.append(f"Temperament: {r['temperament']}")
|
||
return "\n".join(parts)
|
||
|
||
|
||
async def _ki_complete(prompt: str) -> str:
|
||
import ki as ki_module
|
||
return await ki_module.complete(
|
||
prompt,
|
||
system=_SYSTEM,
|
||
max_tokens=1200,
|
||
requires_premium=False,
|
||
)
|
||
|
||
|
||
async def _ki_complete_tracked(prompt: str, user_id: int) -> str:
|
||
"""Wie _ki_complete, zählt die Anfrage in ki_daily_calls (source='luna')."""
|
||
import datetime as _dt
|
||
import ki as ki_module
|
||
text = await ki_module.complete(
|
||
prompt,
|
||
system=_SYSTEM,
|
||
max_tokens=1200,
|
||
requires_premium=False,
|
||
)
|
||
today = str(_dt.date.today())
|
||
try:
|
||
with db() as conn:
|
||
conn.execute("""
|
||
INSERT INTO ki_daily_calls (user_id, date, count, source) VALUES (?, ?, 1, 'luna')
|
||
ON CONFLICT(user_id, date, source) DO UPDATE SET count = count + 1
|
||
""", (user_id, today))
|
||
except Exception:
|
||
pass
|
||
return text
|
||
|
||
|
||
def _parse_json(raw: str) -> dict:
|
||
import re
|
||
try:
|
||
return json.loads(raw)
|
||
except json.JSONDecodeError:
|
||
pass
|
||
m = re.search(r"```(?:json)?\s*([\s\S]+?)\s*```", raw)
|
||
if m:
|
||
try:
|
||
return json.loads(m.group(1))
|
||
except json.JSONDecodeError:
|
||
pass
|
||
m = re.search(r"\{[\s\S]+\}", raw)
|
||
if m:
|
||
try:
|
||
return json.loads(m.group(0))
|
||
except json.JSONDecodeError:
|
||
pass
|
||
raise ValueError(f"Kein JSON in Antwort: {raw[:200]}")
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET /api/social/diversity — Kreativitäts-Check
|
||
# ------------------------------------------------------------------
|
||
@router.get("/diversity")
|
||
async def get_diversity(user=Depends(require_social_media)):
|
||
warning = _diversity_warning()
|
||
with db() as conn:
|
||
rows = conn.execute(
|
||
"""SELECT category, COUNT(*) as n FROM social_content
|
||
WHERE category IS NOT NULL
|
||
ORDER BY n DESC""",
|
||
).fetchall()
|
||
return {
|
||
"warning": warning,
|
||
"distribution": [dict(r) for r in rows],
|
||
}
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET /api/social/suggestions — KI schlägt 5 Themen vor
|
||
# ------------------------------------------------------------------
|
||
@router.get("/suggestions")
|
||
async def get_suggestions(user=Depends(require_social_media)):
|
||
from datetime import date
|
||
import calendar
|
||
|
||
today = date.today()
|
||
month = today.month
|
||
saison = (
|
||
"Frühling" if month in (3,4,5) else
|
||
"Sommer" if month in (6,7,8) else
|
||
"Herbst" if month in (9,10,11) else "Winter"
|
||
)
|
||
prompt = _PROMPT_SUGGESTIONS.format(
|
||
datum=today.strftime("%d.%m.%Y"),
|
||
saison=saison,
|
||
used_topics=_used_topics(20),
|
||
)
|
||
try:
|
||
raw = await _ki_complete(prompt)
|
||
import re
|
||
m = re.search(r"\[[\s\S]+\]", raw)
|
||
data = json.loads(m.group(0)) if m else []
|
||
return data
|
||
except Exception as e:
|
||
logger.error("Suggestions-Fehler: %s", e)
|
||
raise HTTPException(500, f"KI-Fehler: {e}")
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /api/social/ideas/save — Vorschlag als Idee speichern (zurückstellen)
|
||
# ------------------------------------------------------------------
|
||
@router.post("/ideas/save", status_code=201)
|
||
async def save_idea(data: dict, user=Depends(require_social_media)):
|
||
"""Speichert einen KI-Vorschlag als Idee in der DB (status='idea')."""
|
||
topic = (data.get("topic") or data.get("thema") or "").strip()
|
||
platform = data.get("platform", "both")
|
||
fmt = data.get("format", "post")
|
||
category = data.get("category")
|
||
if not topic:
|
||
raise HTTPException(400, "topic fehlt.")
|
||
with db() as conn:
|
||
cur = conn.execute(
|
||
"""INSERT INTO social_content
|
||
(created_by, platform, format, topic, status, category, source)
|
||
VALUES (?,?,?,?,'idea',?,'saved_suggestion')""",
|
||
(user["id"], platform, fmt, topic, category),
|
||
)
|
||
return {"id": cur.lastrowid, "topic": topic, "status": "idea"}
|
||
|
||
|
||
# GET /api/social/content — alle Einträge
|
||
# ------------------------------------------------------------------
|
||
@router.get("/content")
|
||
async def list_content(
|
||
status: Optional[str] = None,
|
||
user=Depends(require_social_media),
|
||
):
|
||
with db() as conn:
|
||
if status:
|
||
rows = conn.execute(
|
||
"SELECT * FROM social_content WHERE status=? ORDER BY created_at DESC",
|
||
(status,),
|
||
).fetchall()
|
||
else:
|
||
rows = conn.execute(
|
||
"SELECT * FROM social_content ORDER BY created_at DESC LIMIT 200",
|
||
).fetchall()
|
||
return [dict(r) for r in rows]
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /api/social/generate — KI-Content generieren
|
||
# ------------------------------------------------------------------
|
||
@router.post("/generate")
|
||
async def generate_content(req: GenerateRequest, user=Depends(require_social_media)):
|
||
if req.platform not in _PLATFORMS:
|
||
raise HTTPException(400, f"Ungültige Plattform: {req.platform}")
|
||
if req.format not in _FORMATS:
|
||
raise HTTPException(400, f"Ungültiges Format: {req.format}")
|
||
if not req.topic.strip():
|
||
raise HTTPException(400, "Thema darf nicht leer sein.")
|
||
|
||
script_field = _SCRIPT_FIELD_REEL if req.format == "reel" else _SCRIPT_FIELD_OTHER
|
||
prompt = _PROMPT_GENERATE.format(
|
||
platform=req.platform,
|
||
format=req.format,
|
||
topic=req.topic,
|
||
breed_info=_breed_info(req.breed_id),
|
||
used_topics=_used_topics(),
|
||
category_stats=_category_stats(),
|
||
script_field=script_field,
|
||
)
|
||
|
||
try:
|
||
raw = await _ki_complete_tracked(prompt, user["id"])
|
||
data = _parse_json(raw)
|
||
except Exception as e:
|
||
logger.error("Social-Media-Generierung fehlgeschlagen: %s", e)
|
||
raise HTTPException(500, f"KI-Fehler: {e}")
|
||
|
||
with db() as conn:
|
||
cur = conn.execute(
|
||
"""INSERT INTO social_content
|
||
(created_by, platform, format, topic, caption, hashtags,
|
||
visual_brief, image_prompt, canva_notes, script, hook, cta,
|
||
unsplash_query, ai_score, source, breed_id, coaching, category)
|
||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||
(
|
||
user["id"], req.platform, req.format, req.topic,
|
||
data.get("caption"), data.get("hashtags"),
|
||
data.get("visual_brief"), data.get("image_prompt"),
|
||
data.get("canva_notes"), data.get("script"),
|
||
data.get("hook"), data.get("cta"),
|
||
data.get("unsplash_query"), data.get("ai_score"),
|
||
"generated", req.breed_id, data.get("coaching"), data.get("category"),
|
||
),
|
||
)
|
||
entry_id = cur.lastrowid
|
||
|
||
with db() as conn:
|
||
row = conn.execute(
|
||
"SELECT * FROM social_content WHERE id=?", (entry_id,)
|
||
).fetchone()
|
||
result = dict(row)
|
||
result["feedback"] = data.get("feedback")
|
||
return result
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /api/social/evaluate — eigenen Entwurf bewerten + verbessern
|
||
# ------------------------------------------------------------------
|
||
@router.post("/evaluate")
|
||
async def evaluate_content(req: EvaluateRequest, user=Depends(require_social_media)):
|
||
if not req.draft.strip():
|
||
raise HTTPException(400, "Entwurf darf nicht leer sein.")
|
||
|
||
prompt = _PROMPT_EVALUATE.format(
|
||
platform=req.platform,
|
||
format=req.format,
|
||
draft=req.draft,
|
||
)
|
||
|
||
try:
|
||
raw = await _ki_complete_tracked(prompt, user["id"])
|
||
data = _parse_json(raw)
|
||
except Exception as e:
|
||
raise HTTPException(500, f"KI-Fehler: {e}")
|
||
|
||
with db() as conn:
|
||
cur = conn.execute(
|
||
"""INSERT INTO social_content
|
||
(created_by, platform, format, topic, caption, hashtags,
|
||
visual_brief, image_prompt, canva_notes, script, hook, cta,
|
||
unsplash_query, ai_score, source, notes)
|
||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||
(
|
||
user["id"], req.platform, req.format,
|
||
req.draft[:80] + ("…" if len(req.draft) > 80 else ""),
|
||
data.get("caption"), data.get("hashtags"),
|
||
data.get("visual_brief"), data.get("image_prompt"),
|
||
data.get("canva_notes"), data.get("script"),
|
||
data.get("hook"), data.get("cta"),
|
||
data.get("unsplash_query"), data.get("ai_score"),
|
||
"user", data.get("feedback"),
|
||
),
|
||
)
|
||
entry_id = cur.lastrowid
|
||
|
||
with db() as conn:
|
||
row = conn.execute(
|
||
"SELECT * FROM social_content WHERE id=?", (entry_id,)
|
||
).fetchone()
|
||
result = dict(row)
|
||
result["feedback"] = data.get("feedback")
|
||
return result
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# PATCH /api/social/content/{id} — Status / Notizen aktualisieren
|
||
# ------------------------------------------------------------------
|
||
@router.patch("/content/{cid}")
|
||
async def update_content(cid: int, data: StatusUpdate, user=Depends(require_social_media)):
|
||
updates = {k: v for k, v in data.model_dump().items() if v is not None}
|
||
if not updates:
|
||
raise HTTPException(400, "Keine Felder zum Aktualisieren.")
|
||
if "status" in updates and updates["status"] not in _STATUSES:
|
||
raise HTTPException(400, f"Ungültiger Status: {updates['status']}")
|
||
|
||
cols = ", ".join(f"{k}=?" for k in updates)
|
||
values = list(updates.values()) + [cid]
|
||
with db() as conn:
|
||
conn.execute(f"UPDATE social_content SET {cols} WHERE id=?", values)
|
||
return {"ok": True}
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# DELETE /api/social/content/{id}
|
||
# ------------------------------------------------------------------
|
||
@router.delete("/content/{cid}", status_code=204)
|
||
async def delete_content(cid: int, user=Depends(require_social_media)):
|
||
with db() as conn:
|
||
conn.execute("DELETE FROM social_content WHERE id=?", (cid,))
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# Übungen in DB seeden (einmalig beim Import)
|
||
# ------------------------------------------------------------------
|
||
def _seed_exercises():
|
||
with db() as conn:
|
||
for u in _UEBUNGEN:
|
||
conn.execute(
|
||
"""INSERT OR IGNORE INTO training_exercises
|
||
(exercise_id, name, kategorie, schwierigkeit, alter_ab,
|
||
dauer, beschreibung, schritte, tipp)
|
||
VALUES (?,?,?,?,?,?,?,?,?)""",
|
||
(u["id"], u["name"], u["kat"], u["schwierigkeit"],
|
||
u.get("alter"), u.get("dauer"), u.get("beschreibung"),
|
||
json.dumps(u.get("schritte", []), ensure_ascii=False),
|
||
u.get("tipp")),
|
||
)
|
||
|
||
def _seed_pflege():
|
||
with db() as conn:
|
||
for p in _PFLEGE_TIPPS:
|
||
conn.execute(
|
||
"""INSERT OR IGNORE INTO pflege_tipps
|
||
(tipp_id, titel, kategorie, beschreibung, schritte,
|
||
materialien, haeufigkeit, fell_typ, saison, rassengruppe, tipp,
|
||
fell_pflege_art)
|
||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||
(p["id"], p["titel"], p["kat"],
|
||
p.get("beschreibung"), json.dumps(p.get("schritte", []), ensure_ascii=False),
|
||
p.get("materialien"), p.get("haeufigkeit"),
|
||
p.get("fell_typ"), p.get("saison"), p.get("rassengruppe"), p.get("tipp"),
|
||
p.get("fell_pflege_art")),
|
||
)
|
||
# UPDATE für bereits bestehende Tipps (idempotent)
|
||
if p.get("fell_pflege_art"):
|
||
conn.execute(
|
||
"UPDATE pflege_tipps SET fell_pflege_art=? WHERE tipp_id=? AND (fell_pflege_art IS NULL OR fell_pflege_art != ?)",
|
||
(p["fell_pflege_art"], p["id"], p["fell_pflege_art"]),
|
||
)
|
||
|
||
try:
|
||
_seed_exercises()
|
||
_seed_pflege()
|
||
except Exception:
|
||
pass
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /api/social/training-tip — Trainingstipp-Post generieren
|
||
# ------------------------------------------------------------------
|
||
@router.post("/training-tip")
|
||
async def training_tip(user=Depends(require_social_media)):
|
||
# Übung wählen die noch nicht als Social-Post verwendet wurde.
|
||
# Per SQL: zuerst eine unbenutzte zufällig wählen, sonst Reset (irgendeine).
|
||
with db() as conn:
|
||
row = conn.execute(
|
||
"""SELECT * FROM training_exercises
|
||
WHERE exercise_id NOT IN (
|
||
SELECT exercise_id FROM social_content WHERE exercise_id IS NOT NULL
|
||
)
|
||
ORDER BY RANDOM() LIMIT 1"""
|
||
).fetchone()
|
||
if not row:
|
||
# Alle durch — Reset: irgendeine zufällige nehmen
|
||
row = conn.execute(
|
||
"SELECT * FROM training_exercises ORDER BY RANDOM() LIMIT 1"
|
||
).fetchone()
|
||
|
||
if not row:
|
||
raise HTTPException(404, "Keine Übungen gefunden.")
|
||
|
||
ex = dict(row)
|
||
stil = random.choice(_TRAINING_STILE)
|
||
schritte_list = json.loads(ex["schritte"] or "[]")
|
||
schritte_text = "\n".join(f" {i+1}. {s}" for i, s in enumerate(schritte_list[:4]))
|
||
|
||
prompt = _PROMPT_TRAINING.format(
|
||
name=ex["name"],
|
||
kat=ex["kategorie"],
|
||
schwierigkeit=ex["schwierigkeit"] or "Anfänger",
|
||
alter=ex["alter_ab"] or "Ab 8 Wochen",
|
||
dauer=ex["dauer"] or "5 Min",
|
||
beschreibung=ex["beschreibung"] or ex["name"],
|
||
schritte=schritte_text,
|
||
tipp=ex["tipp"] or "",
|
||
stil=stil,
|
||
name_lower=ex["name"].lower().replace(" ", ""),
|
||
name_en=ex["name"],
|
||
)
|
||
|
||
try:
|
||
raw = await _ki_complete_tracked(prompt, user["id"])
|
||
data = _parse_json(raw)
|
||
except Exception as e:
|
||
raise HTTPException(500, f"KI-Fehler: {e}")
|
||
|
||
stil_label = {
|
||
"tutorial": f"So geht's: {ex['name']}",
|
||
"community": f"Lernen gerade: {ex['name']} 🙋",
|
||
"aspirational": f"Das kann dein Hund auch: {ex['name']}",
|
||
}[stil]
|
||
|
||
with db() as conn:
|
||
cur = conn.execute(
|
||
"""INSERT INTO social_content
|
||
(created_by, platform, format, topic, caption, hashtags,
|
||
visual_brief, canva_notes, hook, cta, unsplash_query,
|
||
ai_score, source, coaching, category, exercise_id)
|
||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||
(user["id"], "both", "reel" if stil == "tutorial" else "post",
|
||
stil_label, data.get("caption"), data.get("hashtags"),
|
||
data.get("visual_brief"), data.get("canva_notes"),
|
||
data.get("hook"), data.get("cta"), data.get("unsplash_query"),
|
||
data.get("ai_score"), "generated", data.get("coaching"),
|
||
"training", ex["exercise_id"]),
|
||
)
|
||
entry_id = cur.lastrowid
|
||
|
||
with db() as conn:
|
||
row = conn.execute("SELECT * FROM social_content WHERE id=?", (entry_id,)).fetchone()
|
||
result = dict(row)
|
||
result["exercise_name"] = ex["name"]
|
||
result["exercise_kat"] = ex["kategorie"]
|
||
result["stil"] = stil
|
||
return result
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET /api/social/exercises — alle Übungen mit Nutzungsstatistik
|
||
# ------------------------------------------------------------------
|
||
@router.get("/exercises")
|
||
async def get_exercises(user=Depends(require_social_media)):
|
||
with db() as conn:
|
||
rows = conn.execute(
|
||
"""SELECT e.*,
|
||
(SELECT COUNT(*) FROM social_content s
|
||
WHERE s.exercise_id = e.exercise_id) as posts_count
|
||
FROM training_exercises e
|
||
ORDER BY e.kategorie, e.name"""
|
||
).fetchall()
|
||
return [dict(r) for r in rows]
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET /api/social/breeds — alle Rassen für Autocomplete
|
||
# ------------------------------------------------------------------
|
||
@router.get("/breeds")
|
||
async def social_breeds(user=Depends(require_social_media)):
|
||
with db() as conn:
|
||
rows = conn.execute(
|
||
"SELECT id, name FROM wiki_rassen WHERE ki_enriched=1 ORDER BY name ASC"
|
||
).fetchall()
|
||
return [dict(r) for r in rows]
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET /api/social/unused-breeds — noch nie in Post verwendete Rassen
|
||
# ------------------------------------------------------------------
|
||
@router.get("/unused-breeds")
|
||
async def unused_breeds(limit: int = 6, user=Depends(require_social_media)):
|
||
with db() as conn:
|
||
rows = conn.execute(
|
||
"""SELECT r.id, r.name FROM wiki_rassen r
|
||
WHERE r.ki_enriched = 1
|
||
AND r.id NOT IN (
|
||
SELECT breed_id FROM social_content
|
||
WHERE breed_id IS NOT NULL
|
||
)
|
||
ORDER BY RANDOM()
|
||
LIMIT ?""",
|
||
(limit,),
|
||
).fetchall()
|
||
return [dict(r) for r in rows]
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /api/social/breed-of-day — "Wusstest du schon?" Post generieren
|
||
# ------------------------------------------------------------------
|
||
_PROMPT_BREED_DAY = '''\
|
||
Erstelle einen begeisternden Social-Media-Post über die Hunderasse "{name}" für Ban Yaro (banyaro.app).
|
||
|
||
Rassen-Daten aus unserer Datenbank:
|
||
- Herkunft: {herkunft}
|
||
- Größe: {groesse}
|
||
- Gewicht: {gewicht_min}–{gewicht_max} kg
|
||
- Lebensdauer: {lebensdauer}
|
||
- Aktivität: {aktivitaet}
|
||
- Erfahrung: {erfahrung}
|
||
- Temperament: {temperament}
|
||
- Für Kinder geeignet: {kinder}
|
||
- Wohnungsgeeignet: {wohnung}
|
||
- Beschreibung: {beschreibung}
|
||
|
||
Antworte NUR als JSON:
|
||
{{
|
||
"caption": "Post-Text der mit 🐾 Wusstest du schon, wie cool der/die {name} ist? beginnt. Fakten aus den Daten, mit Emojis, max 800 Zeichen.",
|
||
"hashtags": "10-12 Hashtags kommagetrennt ohne #: Rassenname, hundeleben, banyaro, passende Eigenschaften",
|
||
"hook": "Erste Zeile als Aufmerksamkeitsfänger",
|
||
"cta": "Frage ans Publikum passend zur Rasse",
|
||
"visual_brief": "Beschreibung: typisches Bild dieser Rasse, Stimmung, Setting",
|
||
"canva_notes": "Textüberlagerungen für das Rassenfoto: Rassenname groß, 1-2 Fakten",
|
||
"unsplash_query": "2-3 englische Suchbegriffe für diese Rasse",
|
||
"ai_score": <1-5>,
|
||
"category": "rasse",
|
||
"coaching": "Tipp für die Creatorin: Warum ist diese Rasse spannend für die Zielgruppe? (1-2 Sätze)"
|
||
}}
|
||
'''
|
||
|
||
@router.post("/breed-of-day")
|
||
async def breed_of_day(user=Depends(require_social_media)):
|
||
with db() as conn:
|
||
rasse = conn.execute(
|
||
"""SELECT r.id, r.name, r.herkunft, r.groesse,
|
||
r.gewicht_min_kg, r.gewicht_max_kg, r.lebensdauer,
|
||
r.aktivitaet, r.erfahrung, r.temperament,
|
||
r.kinder_geeignet, r.wohnung_geeignet,
|
||
r.beschreibung, r.foto_url
|
||
FROM wiki_rassen r
|
||
WHERE r.ki_enriched = 1
|
||
AND r.beschreibung IS NOT NULL
|
||
AND r.id NOT IN (
|
||
SELECT breed_id FROM social_content WHERE breed_id IS NOT NULL
|
||
)
|
||
ORDER BY RANDOM()
|
||
LIMIT 1""",
|
||
).fetchone()
|
||
|
||
if not rasse:
|
||
raise HTTPException(404, "Alle Rassen wurden bereits verwendet — Reset nötig.")
|
||
|
||
def _yn(v): return "Ja" if v else "Nein"
|
||
prompt = _PROMPT_BREED_DAY.format(
|
||
name=rasse["name"],
|
||
herkunft=rasse["herkunft"] or "unbekannt",
|
||
groesse=rasse["groesse"] or "unbekannt",
|
||
gewicht_min=rasse["gewicht_min_kg"] or "?",
|
||
gewicht_max=rasse["gewicht_max_kg"] or "?",
|
||
lebensdauer=rasse["lebensdauer"] or "unbekannt",
|
||
aktivitaet=rasse["aktivitaet"] or "mittel",
|
||
erfahrung=rasse["erfahrung"] or "fortgeschritten",
|
||
temperament=rasse["temperament"] or "unbekannt",
|
||
kinder=_yn(rasse["kinder_geeignet"]),
|
||
wohnung=_yn(rasse["wohnung_geeignet"]),
|
||
beschreibung=(rasse["beschreibung"] or "")[:400],
|
||
)
|
||
|
||
try:
|
||
raw = await _ki_complete_tracked(prompt, user["id"])
|
||
data = _parse_json(raw)
|
||
except Exception as e:
|
||
raise HTTPException(500, f"KI-Fehler: {e}")
|
||
|
||
with db() as conn:
|
||
cur = conn.execute(
|
||
"""INSERT INTO social_content
|
||
(created_by, platform, format, topic, caption, hashtags,
|
||
visual_brief, image_prompt, canva_notes, hook, cta,
|
||
unsplash_query, ai_score, source, breed_id, coaching,
|
||
category, media_url)
|
||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||
(
|
||
user["id"], "both", "post",
|
||
f"Rasse des Tages: {rasse['name']}",
|
||
data.get("caption"), data.get("hashtags"),
|
||
data.get("visual_brief"), None,
|
||
data.get("canva_notes"), data.get("hook"), data.get("cta"),
|
||
data.get("unsplash_query"), data.get("ai_score"),
|
||
"generated", rasse["id"], data.get("coaching"),
|
||
"rasse", rasse["foto_url"],
|
||
),
|
||
)
|
||
entry_id = cur.lastrowid
|
||
|
||
with db() as conn:
|
||
row = conn.execute("SELECT * FROM social_content WHERE id=?", (entry_id,)).fetchone()
|
||
result = dict(row)
|
||
result["breed_foto"] = rasse["foto_url"]
|
||
return result
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET /api/social/stats — Level / XP
|
||
# ------------------------------------------------------------------
|
||
_LEVELS = [
|
||
(0, 50, "🌱 Rookie", "Anfängerin"),
|
||
(50, 150, "✏️ Creator", "Content Creatorin"),
|
||
(150, 400, "🌟 Influencer", "Influencerin"),
|
||
(400, 800, "🏆 Pro Creator", "Profi"),
|
||
(800, 99999,"👑 Star", "Social Media Star"),
|
||
]
|
||
|
||
@router.get("/stats")
|
||
async def get_stats(user=Depends(require_social_media)):
|
||
with db() as conn:
|
||
generated = conn.execute(
|
||
"SELECT COUNT(*) FROM social_content WHERE source='generated'"
|
||
).fetchone()[0]
|
||
published = conn.execute(
|
||
"SELECT COUNT(*) FROM social_content WHERE status='published'"
|
||
).fetchone()[0]
|
||
evaluated = conn.execute(
|
||
"SELECT COUNT(*) FROM social_content WHERE source='user'"
|
||
).fetchone()[0]
|
||
five_star = conn.execute(
|
||
"SELECT COUNT(*) FROM social_content WHERE ai_score=5"
|
||
).fetchone()[0]
|
||
|
||
xp = generated * 10 + published * 20 + evaluated * 5 + five_star * 15
|
||
lvl = _LEVELS[0]
|
||
for l in _LEVELS:
|
||
if xp >= l[0]:
|
||
lvl = l
|
||
idx = _LEVELS.index(lvl)
|
||
nxt = _LEVELS[idx + 1] if idx < len(_LEVELS) - 1 else None
|
||
|
||
return {
|
||
"xp": xp, "generated": generated, "published": published,
|
||
"evaluated": evaluated, "five_star": five_star,
|
||
"level": lvl[2], "level_desc": lvl[3],
|
||
"xp_current_min": lvl[0], "xp_next": nxt[0] if nxt else xp,
|
||
"next_level": nxt[2] if nxt else None,
|
||
}
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /api/social/pflege-tipp — Pflegetipp generieren (allg. oder rassenspezifisch)
|
||
# ------------------------------------------------------------------
|
||
@router.post("/pflege-tipp")
|
||
async def pflege_tipp(breed_id: Optional[int] = None, user=Depends(require_social_media)):
|
||
with db() as conn:
|
||
used_ids = {r["exercise_id"] for r in conn.execute(
|
||
"SELECT exercise_id FROM social_content WHERE exercise_id IS NOT NULL"
|
||
).fetchall()}
|
||
|
||
# Rassenspezifische Tipps bevorzugen wenn breed_id angegeben
|
||
rasse = None
|
||
if breed_id:
|
||
rasse = conn.execute(
|
||
"SELECT name, groesse, beschreibung, herkunft FROM wiki_rassen WHERE id=?",
|
||
(breed_id,),
|
||
).fetchone()
|
||
|
||
# LIMIT 100 deckelt das Result-Set ab (Tabelle hat aktuell ~43 Einträge);
|
||
# der Python-Filter unten braucht mehrere Kandidaten für Fell-Typ-Auswahl.
|
||
tipps = conn.execute(
|
||
"SELECT * FROM pflege_tipps ORDER BY RANDOM() LIMIT 100"
|
||
).fetchall()
|
||
|
||
# Noch nicht verwendete bevorzugen
|
||
unused = [t for t in tipps if t["tipp_id"] not in used_ids]
|
||
pool = unused if unused else list(tipps)
|
||
|
||
# Bei Rasse: Fell-Typ-Filter wenn möglich
|
||
if rasse and rasse["groesse"]:
|
||
fell_filter = "kurz" if rasse["groesse"] in ("klein", "mittel") else "lang"
|
||
relevant = [t for t in pool if not t["fell_typ"] or t["fell_typ"] == "alle"
|
||
or fell_filter in (t["fell_typ"] or "")]
|
||
if relevant:
|
||
pool = relevant
|
||
|
||
if not pool:
|
||
raise HTTPException(404, "Keine Pflegetipps gefunden.")
|
||
|
||
t = dict(pool[0])
|
||
schritte_list = json.loads(t["schritte"] or "[]")
|
||
schritte_text = "\n".join(f" {i+1}. {s}" for i, s in enumerate(schritte_list[:5]))
|
||
|
||
rasse_kontext = ""
|
||
if rasse:
|
||
rasse_kontext = (
|
||
f"\nRassenspezifisch für: {rasse['name']} "
|
||
f"(Größe: {rasse['groesse'] or 'unbekannt'})\n"
|
||
"Passe den Post gezielt an diese Rasse an."
|
||
)
|
||
|
||
prompt = _PROMPT_PFLEGE.format(
|
||
titel=t["titel"],
|
||
kat=t["kategorie"],
|
||
beschreibung=t["beschreibung"] or t["titel"],
|
||
schritte=schritte_text,
|
||
materialien=t["materialien"] or "Standard Pflegeutensilien",
|
||
haeufigkeit=t["haeufigkeit"] or "Nach Bedarf",
|
||
tipp=t["tipp"] or "",
|
||
rasse_kontext=rasse_kontext,
|
||
kat_lower=t["kategorie"].lower().replace(" ", "").replace("-", ""),
|
||
)
|
||
|
||
try:
|
||
raw = await _ki_complete_tracked(prompt, user["id"])
|
||
data = _parse_json(raw)
|
||
except Exception as e:
|
||
raise HTTPException(500, f"KI-Fehler: {e}")
|
||
|
||
topic = f"Pflegetipp: {t['titel']}"
|
||
if rasse:
|
||
topic += f" ({rasse['name']})"
|
||
|
||
with db() as conn:
|
||
cur = conn.execute(
|
||
"""INSERT INTO social_content
|
||
(created_by, platform, format, topic, caption, hashtags,
|
||
visual_brief, canva_notes, hook, cta, unsplash_query,
|
||
ai_score, source, coaching, category, exercise_id)
|
||
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)""",
|
||
(user["id"], "both", "post", topic,
|
||
data.get("caption"), data.get("hashtags"),
|
||
data.get("visual_brief"), data.get("canva_notes"),
|
||
data.get("hook"), data.get("cta"), data.get("unsplash_query"),
|
||
data.get("ai_score"), "generated", data.get("coaching"),
|
||
"pflege", t["tipp_id"]),
|
||
)
|
||
entry_id = cur.lastrowid
|
||
|
||
with db() as conn:
|
||
row = conn.execute("SELECT * FROM social_content WHERE id=?", (entry_id,)).fetchone()
|
||
result = dict(row)
|
||
result["pflege_titel"] = t["titel"]
|
||
result["pflege_kat"] = t["kategorie"]
|
||
result["rasse_name"] = rasse["name"] if rasse else None
|
||
return result
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# GET /api/social/pflege-kategorien — Statistik Pflegetipps
|
||
# ------------------------------------------------------------------
|
||
@router.get("/pflege-kategorien")
|
||
async def pflege_kategorien(user=Depends(require_social_media)):
|
||
with db() as conn:
|
||
rows = conn.execute(
|
||
"""SELECT p.kategorie, COUNT(*) as total,
|
||
SUM(CASE WHEN sc.exercise_id IS NOT NULL THEN 1 ELSE 0 END) as used
|
||
FROM pflege_tipps p
|
||
LEFT JOIN social_content sc ON sc.exercise_id = p.tipp_id
|
||
GROUP BY p.kategorie ORDER BY p.kategorie"""
|
||
).fetchall()
|
||
return [dict(r) for r in rows]
|
||
|
||
|
||
# ------------------------------------------------------------------
|
||
# POST /api/social/media — Medien-Upload (Foto/Video)
|
||
# ------------------------------------------------------------------
|
||
@router.post("/media")
|
||
async def upload_media(
|
||
file: UploadFile = File(...),
|
||
user=Depends(require_social_media),
|
||
):
|
||
import shutil, uuid, os
|
||
|
||
MEDIA_DIR = os.getenv("MEDIA_DIR", "/data/media")
|
||
social_dir = os.path.join(MEDIA_DIR, "social")
|
||
os.makedirs(social_dir, exist_ok=True)
|
||
|
||
ext = os.path.splitext(file.filename or "")[-1].lower() or ".jpg"
|
||
fname = f"{uuid.uuid4().hex}{ext}"
|
||
path = os.path.join(social_dir, fname)
|
||
|
||
with open(path, "wb") as f:
|
||
shutil.copyfileobj(file.file, f)
|
||
|
||
return {"url": f"/media/social/{fname}", "filename": fname}
|