banyaro/backend/routes/movies.py
rene 097295c628 Sprint 11: Freunde & Chat + Phosphor-Icon-Vollmigration
- Freundschaften (pending/accepted), Nutzersuche, Anfragen per Push
- Direktnachrichten mit Polling, iMessage-Stil, Deep-Links aus Push
- Alle Seiten (map, places, diary, health, dog-profile, sitting, knigge,
  forum, wiki, walks) vollständig auf Phosphor-Icons migriert
- Wikidata-Rassen-Scraper (~833 neue Rassen, lokal gespiegelte Fotos)
- TheDogAPI lokal gespiegelt (169 Rassen + Fotos)
- Quiz-Result-Cards horizontal (korrekte Bildproportionen)
- SW by-v89
2026-04-15 21:33:53 +02:00

185 lines
10 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""BAN YARO — Hunde-Filme Routes"""
from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from typing import Optional
from datetime import datetime
from database import db
from auth import get_current_user, get_current_user_optional
router = APIRouter()
# ------------------------------------------------------------------
# Hardcoded Film-Daten
# ------------------------------------------------------------------
FILME = [
{"id": "lassie", "titel": "Lassie", "jahr": 1943, "genre": "Familie", "hund_rasse": "Collie", "stirbt_der_hund": False, "beschreibung": "Der Klassiker schlechthin. Lassie findet immer nach Hause.", "bild_emoji": "🐕", "bewertung_avg": 4.2},
{"id": "benji", "titel": "Benji", "jahr": 1974, "genre": "Familie", "hund_rasse": "Mischling", "stirbt_der_hund": False, "beschreibung": "Ein herrenloser Hund rettet Kinder aus den Händen von Entführern.", "bild_emoji": "🐾", "bewertung_avg": 4.0},
{"id": "marley-and-me", "titel": "Marley & Ich", "jahr": 2008, "genre": "Drama/Komödie", "hund_rasse": "Labrador", "stirbt_der_hund": True, "beschreibung": "Der chaotischste, aber liebste Labrador der Welt. Achtung: Taschentücher bereithalten.", "bild_emoji": "😭", "bewertung_avg": 4.5},
{"id": "hachiko", "titel": "Hachi: A Dog's Tale", "jahr": 2009, "genre": "Drama", "hund_rasse": "Akita", "stirbt_der_hund": True, "beschreibung": "Basiert auf der wahren Geschichte des treuen Akita Hachikō. Starke emotionale Wirkung.", "bild_emoji": "💔", "bewertung_avg": 4.8},
{"id": "101-dalmatiner", "titel": "101 Dalmatiner", "jahr": 1961, "genre": "Animation/Familie", "hund_rasse": "Dalmatiner", "stirbt_der_hund": False, "beschreibung": "Dalmatiner-Welpen vs. die böse Cruella de Vil. Animationsklassiker.", "bild_emoji": "🐡", "bewertung_avg": 4.3},
{"id": "beethoven", "titel": "Beethoven", "jahr": 1992, "genre": "Familie/Komödie", "hund_rasse": "Bernhardiner", "stirbt_der_hund": False, "beschreibung": "Riesiger Bernhardiner bringt Chaos ins Familienleben. Mehrere Fortsetzungen.", "bild_emoji": "🎵", "bewertung_avg": 3.8},
{"id": "rex", "titel": "Kommissar Rex", "jahr": 1994, "genre": "Krimi/Serie", "hund_rasse": "Deutscher Schäferhund", "stirbt_der_hund": False, "beschreibung": "Österreichische Krimiserie. Rex löst gemeinsam mit seinem Herrchen Verbrechen.", "bild_emoji": "🔍", "bewertung_avg": 4.1},
{"id": "old-yeller", "titel": "Old Yeller", "jahr": 1957, "genre": "Familie/Drama", "hund_rasse": "Mischling", "stirbt_der_hund": True, "beschreibung": "Amerikanischer Filmklassiker. Berühmtestes Filmende der Hundfilm-Geschichte.", "bild_emoji": "🌾", "bewertung_avg": 4.0},
{"id": "buddy", "titel": "Air Bud", "jahr": 1997, "genre": "Familie/Sport", "hund_rasse": "Golden Retriever", "stirbt_der_hund": False, "beschreibung": "Hund spielt Basketball. Klingt absurd, wurde ein Hit.", "bild_emoji": "🏀", "bewertung_avg": 3.5},
{"id": "john-wick", "titel": "John Wick", "jahr": 2014, "genre": "Action", "hund_rasse": "Beagle", "stirbt_der_hund": True, "beschreibung": "Achtung Spoiler: Der Hund stirbt am Anfang. Das löst die ganze Geschichte aus. Kontroversiell beliebt.", "bild_emoji": "💣", "bewertung_avg": 4.6},
{"id": "isle-of-dogs", "titel": "Isle of Dogs", "jahr": 2018, "genre": "Animation", "hund_rasse": "Verschiedene", "stirbt_der_hund": False, "beschreibung": "Wes Anderson Stopmotion-Meisterwerk. Alle Hunde Japans auf einer Insel verbannt.", "bild_emoji": "🏝️", "bewertung_avg": 4.4},
{"id": "eight-below", "titel": "8 Below", "jahr": 2006, "genre": "Abenteuer/Drama", "hund_rasse": "Schlittenhunde", "stirbt_der_hund": True, "beschreibung": "Basiert auf wahren Ereignissen. Schlittenhunde überleben die Antarktis. Einige nicht.", "bild_emoji": "❄️", "bewertung_avg": 4.3},
]
PROMIS = [
{"name": "Hachikō", "rasse": "Akita Inu", "bekannt_fuer": "9 Jahre lang täglich auf seinen verstorbenen Herrchen am Bahnhof Shibuya gewartet. Statue in Tokio.", "emoji": "🗿"},
{"name": "Rin Tin Tin", "rasse": "Deutscher Schäferhund", "bekannt_fuer": "Filmhund der 1920er-Jahre. Rettete Warner Bros. vor dem Bankrott. Erster Hundestar Hollywoods.", "emoji": "🎬"},
{"name": "Laika", "rasse": "Mischling", "bekannt_fuer": "Erstes Lebewesen im Weltall (Sputnik 2, 1957). Wurde zur sowjetischen Weltraumpionierin.", "emoji": "🚀"},
{"name": "Endal", "rasse": "Labrador", "bekannt_fuer": "Assistenzhund in England. Erster Hund der eine EC-Karte am Geldautomaten benutzte.", "emoji": "💳"},
{"name": "Barry", "rasse": "Bernhardiner", "bekannt_fuer": "Legendärer Rettungshund der Alpen (18001812). Soll 40 Menschen das Leben gerettet haben.", "emoji": "🏔️"},
{"name": "Greyfriars Bobby", "rasse": "Skye Terrier", "bekannt_fuer": "14 Jahre lang das Grab seines Herrchens in Edinburgh bewacht. Statue und Pub benannt nach ihm.", "emoji": ""},
]
# ------------------------------------------------------------------
# Schemas
# ------------------------------------------------------------------
class FilmVoteRequest(BaseModel):
bewertung: int # 15
class HundDesMonatsVoteRequest(BaseModel):
dog_id: int
# ------------------------------------------------------------------
# GET /api/movies/filme — Film-Liste mit optionaler User-Bewertung
# ------------------------------------------------------------------
@router.get("/filme")
async def get_filme(user=Depends(get_current_user_optional)):
user_ratings = {}
community_avgs = {}
with db() as conn:
if user:
rows = conn.execute(
"SELECT film_id, bewertung FROM movie_votes WHERE user_id=?",
(user["id"],),
).fetchall()
user_ratings = {r["film_id"]: r["bewertung"] for r in rows}
avg_rows = conn.execute(
"SELECT film_id, AVG(bewertung) as avg_bew, COUNT(*) as cnt FROM movie_votes GROUP BY film_id"
).fetchall()
community_avgs = {r["film_id"]: {"avg": round(r["avg_bew"], 1), "cnt": r["cnt"]} for r in avg_rows}
result = []
for film in FILME:
f = dict(film)
f["user_rating"] = user_ratings.get(film["id"])
if film["id"] in community_avgs:
f["bewertung_avg"] = community_avgs[film["id"]]["avg"]
f["bewertung_cnt"] = community_avgs[film["id"]]["cnt"]
else:
f["bewertung_cnt"] = 0
result.append(f)
return result
# ------------------------------------------------------------------
# POST /api/movies/filme/{film_id}/vote — Bewertung abgeben (Upsert)
# ------------------------------------------------------------------
@router.post("/filme/{film_id}/vote")
async def vote_film(film_id: str, data: FilmVoteRequest, user=Depends(get_current_user)):
if not any(f["id"] == film_id for f in FILME):
raise HTTPException(404, "Film nicht gefunden.")
if data.bewertung < 1 or data.bewertung > 5:
raise HTTPException(400, "Bewertung muss zwischen 1 und 5 liegen.")
with db() as conn:
conn.execute(
"""INSERT INTO movie_votes (user_id, film_id, bewertung)
VALUES (?, ?, ?)
ON CONFLICT(user_id, film_id) DO UPDATE SET bewertung=excluded.bewertung""",
(user["id"], film_id, data.bewertung),
)
row = conn.execute(
"SELECT AVG(bewertung) as avg_bew, COUNT(*) as cnt FROM movie_votes WHERE film_id=?",
(film_id,),
).fetchone()
return {
"film_id": film_id,
"bewertung_avg": round(row["avg_bew"], 1) if row["avg_bew"] else data.bewertung,
"bewertung_cnt": row["cnt"],
"user_rating": data.bewertung,
}
# ------------------------------------------------------------------
# GET /api/movies/hund-des-monats — Top-Votes des aktuellen Monats
# ------------------------------------------------------------------
@router.get("/hund-des-monats")
async def get_hund_des_monats(user=Depends(get_current_user_optional)):
monat = datetime.now().strftime("%Y-%m")
with db() as conn:
rows = conn.execute(
"""SELECT d.id, d.name, d.rasse, d.foto_url, u.name as besitzer_name,
COUNT(v.id) as stimmen
FROM hund_des_monats_votes v
JOIN dogs d ON d.id = v.dog_id
JOIN users u ON u.id = d.user_id
WHERE v.monat = ?
GROUP BY v.dog_id
ORDER BY stimmen DESC
LIMIT 10""",
(monat,),
).fetchall()
user_vote = None
if user:
row = conn.execute(
"SELECT dog_id FROM hund_des_monats_votes WHERE user_id=? AND monat=?",
(user["id"], monat),
).fetchone()
if row:
user_vote = row["dog_id"]
return {
"monat": monat,
"top": [dict(r) for r in rows],
"user_vote": user_vote,
}
# ------------------------------------------------------------------
# POST /api/movies/hund-des-monats/vote — Abstimmen (Auth required)
# ------------------------------------------------------------------
@router.post("/hund-des-monats/vote")
async def vote_hund_des_monats(data: HundDesMonatsVoteRequest, user=Depends(get_current_user)):
monat = datetime.now().strftime("%Y-%m")
with db() as conn:
# Prüfen ob Hund existiert und entweder dem User gehört oder öffentlich ist
dog = conn.execute(
"SELECT id, user_id, is_public FROM dogs WHERE id=?",
(data.dog_id,),
).fetchone()
if not dog:
raise HTTPException(404, "Hund nicht gefunden.")
if dog["user_id"] != user["id"] and not dog["is_public"]:
raise HTTPException(403, "Dieser Hund ist nicht öffentlich.")
conn.execute(
"""INSERT INTO hund_des_monats_votes (user_id, dog_id, monat)
VALUES (?, ?, ?)
ON CONFLICT(user_id, monat) DO UPDATE SET dog_id=excluded.dog_id""",
(user["id"], data.dog_id, monat),
)
# Aktuelle Stimmenanzahl für den gewählten Hund
row = conn.execute(
"SELECT COUNT(*) as cnt FROM hund_des_monats_votes WHERE dog_id=? AND monat=?",
(data.dog_id, monat),
).fetchone()
return {"dog_id": data.dog_id, "monat": monat, "stimmen": row["cnt"]}