""" 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."}, ] _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": , "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 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, ) 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}") # ------------------------------------------------------------------ # 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(prompt) 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(prompt) 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")), ) try: _seed_exercises() 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 with db() as conn: used = {r["exercise_id"] for r in conn.execute( "SELECT exercise_id FROM social_content WHERE exercise_id IS NOT NULL" ).fetchall()} all_ex = conn.execute( "SELECT * FROM training_exercises ORDER BY RANDOM()" ).fetchall() unused = [e for e in all_ex if e["exercise_id"] not in used] pool = unused if unused else list(all_ex) # Reset wenn alle durch if not pool: raise HTTPException(404, "Keine Übungen gefunden.") ex = dict(pool[0]) 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(prompt) 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(prompt) 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/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}