Feature: Sprint31 — 9 Features merged (Streak, Ausgaben, KI-Tierarzt, Rückrufe, Adoption, Vet+Befunde, Hundepass, Playdate, Rassenerkennung)

- Trainings-Streak: streak.py, DB training_streaks, Scheduler 19:00, Widget in welcome.js, Ping in uebungen.js
- Ausgaben-Tracker: expenses.py, expenses.js, DB expenses-Tabelle
- KI-Tierarztfragen: ki.py /tierarzt, health.js Button+Modal, DB ki_tierarzt_log
- Rückruf-Alarm: recalls.py, recalls.js, DB feed_recalls, Scheduler 08:00 RASFF
- Adoption: adoption.py, adoption.js, DB adoption_cache
- Tierarzt-Favorit + Befunde: tieraerzte.py /my-favorite+/favorite, health_docs.py, health.js, api.js, DB favorite_vets+health_documents
- Digitaler Hundepass: passport.py, dog-profile.js, main.py /pass/{token}, DB vaccinations+medications+dog_passport_meta+passport_shares, requirements.txt fpdf2
- Playdate-Matching: playdate.py, playdate.js, DB playdate_listings+playdate_requests
- Rassen-Erkennung: ki.py /rasse-erkennung (Claude Vision), dog-profile.js+wiki.js, CSS .rasse-result-card, DB ki_rasse_log
This commit is contained in:
rene 2026-05-02 09:29:48 +02:00
parent 031c6028ac
commit 742ad189e8
26 changed files with 5734 additions and 27 deletions

View file

@ -1657,3 +1657,203 @@ def _migrate(conn_factory):
);
CREATE INDEX IF NOT EXISTS idx_hdm_wins_dog ON hund_des_monats_wins(dog_id);
""")
# Trainings-Streak-Tabelle
conn.execute("""
CREATE TABLE IF NOT EXISTS training_streaks (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
current_streak INTEGER NOT NULL DEFAULT 0,
longest_streak INTEGER NOT NULL DEFAULT 0,
last_training_date TEXT,
UNIQUE(user_id, dog_id)
)
""")
conn.execute("CREATE INDEX IF NOT EXISTS idx_streaks_user ON training_streaks(user_id)")
# Ausgaben-Tracker
conn.executescript("""
CREATE TABLE IF NOT EXISTS expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
dog_id INTEGER REFERENCES dogs(id) ON DELETE SET NULL,
kategorie TEXT NOT NULL,
betrag REAL NOT NULL,
datum TEXT NOT NULL,
notiz TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_expenses_user ON expenses(user_id, datum DESC);
""")
# KI-Tierarztfragen Rate-Limit-Log
conn.execute("""
CREATE TABLE IF NOT EXISTS ki_tierarzt_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
dog_id INTEGER,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
# KI Rassen-Erkennungs-Log (Rate-Limit: 10/Tag pro User)
conn.execute("""
CREATE TABLE IF NOT EXISTS ki_rasse_log (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.execute("""
CREATE INDEX IF NOT EXISTS idx_ki_rasse_log_user
ON ki_rasse_log(user_id, created_at DESC)
""")
# feed_recalls — Rückruf-Alarm für Tierfutter (RASFF)
conn.execute("""
CREATE TABLE IF NOT EXISTS feed_recalls (
id INTEGER PRIMARY KEY AUTOINCREMENT,
external_id TEXT NOT NULL UNIQUE,
titel TEXT NOT NULL,
produkt TEXT,
gefahr TEXT,
herkunft TEXT,
datum TEXT NOT NULL,
quelle TEXT NOT NULL DEFAULT 'rasff',
url TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.execute("CREATE INDEX IF NOT EXISTS idx_recalls_datum ON feed_recalls(datum DESC)")
# Adoption-Cache
conn.execute("""
CREATE TABLE IF NOT EXISTS adoption_cache (
id INTEGER PRIMARY KEY AUTOINCREMENT,
external_id TEXT NOT NULL UNIQUE,
name TEXT NOT NULL,
rasse TEXT,
alter_jahre REAL,
geschlecht TEXT,
foto_url TEXT,
tierheim TEXT,
tierheim_plz TEXT,
tierheim_lat REAL,
tierheim_lon REAL,
adoptions_url TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
expires_at TEXT NOT NULL
)
""")
# ---- Favoriten-Tierarzt + Gesundheitsdokumente ----
conn.execute("""
CREATE TABLE IF NOT EXISTS favorite_vets (
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
vet_id INTEGER NOT NULL REFERENCES tieraerzte(id) ON DELETE CASCADE,
PRIMARY KEY (user_id, vet_id)
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS health_documents (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
typ TEXT NOT NULL,
titel TEXT NOT NULL,
beschreibung TEXT,
file_path TEXT NOT NULL,
file_type TEXT NOT NULL,
datum TEXT,
vet_id INTEGER REFERENCES tieraerzte(id),
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.execute("CREATE INDEX IF NOT EXISTS idx_health_docs_dog ON health_documents(dog_id, created_at DESC)")
# Digitaler Hundepass — Impfungen, Medikamente, Metadaten, Share-Links
try:
conn.execute("""
CREATE TABLE IF NOT EXISTS vaccinations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
krankheit TEXT NOT NULL,
datum TEXT NOT NULL,
naechste TEXT,
tierarzt TEXT,
charge_nr TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS medications (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
name TEXT NOT NULL,
dosierung TEXT,
von TEXT,
bis TEXT,
notiz TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS dog_passport_meta (
dog_id INTEGER PRIMARY KEY REFERENCES dogs(id) ON DELETE CASCADE,
blutgruppe TEXT,
allergien TEXT,
besonderheiten TEXT,
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS passport_shares (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
token TEXT NOT NULL UNIQUE,
valid_until TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
)
""")
conn.execute("""
CREATE INDEX IF NOT EXISTS idx_passport_shares_token ON passport_shares(token)
""")
logger.info("Migration: Hundepass-Tabellen bereit.")
except Exception as e:
logger.warning(f"Migration Hundepass: {e}")
# ---- Playdate ----
conn.execute("""
CREATE TABLE IF NOT EXISTS playdate_listings (
id INTEGER PRIMARY KEY AUTOINCREMENT,
dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
lat REAL NOT NULL,
lon REAL NOT NULL,
ort_name TEXT,
radius_km INTEGER NOT NULL DEFAULT 10,
beschreibung TEXT,
aktiv INTEGER NOT NULL DEFAULT 1,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(dog_id)
)
""")
conn.execute("""
CREATE INDEX IF NOT EXISTS idx_playdate_listings_geo
ON playdate_listings(lat, lon) WHERE aktiv=1
""")
conn.execute("""
CREATE TABLE IF NOT EXISTS playdate_requests (
id INTEGER PRIMARY KEY AUTOINCREMENT,
from_dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
to_dog_id INTEGER NOT NULL REFERENCES dogs(id) ON DELETE CASCADE,
from_user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
to_user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'pending',
nachricht TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(from_dog_id, to_dog_id)
)
""")