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
This commit is contained in:
rene 2026-04-15 21:33:53 +02:00
parent 96bd57f0ad
commit 097295c628
44 changed files with 9980 additions and 300 deletions

View file

@ -307,6 +307,23 @@ def init_db():
);
CREATE INDEX IF NOT EXISTS idx_osm_pois_loc ON osm_pois(type, lat, lon);
-- VERLORENE HUNDE
CREATE TABLE IF NOT EXISTS lost_dogs (
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,
name TEXT NOT NULL,
rasse TEXT,
beschreibung TEXT NOT NULL,
foto_url TEXT,
lat REAL NOT NULL,
lon REAL NOT NULL,
is_active INTEGER NOT NULL DEFAULT 1,
gefunden_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_lost_active ON lost_dogs(is_active, created_at DESC);
-- OSM Tile-Cache: welche Kacheln wurden schon geladen?
CREATE TABLE IF NOT EXISTS osm_tiles (
type TEXT NOT NULL,
@ -401,6 +418,27 @@ def _migrate(conn_factory):
("osm_pois", "opening_hours", "TEXT"),
("osm_pois", "phone", "TEXT"),
("osm_pois", "website", "TEXT"),
# Forum: Threads brauchen text + antworten-Zähler
("forum_threads", "text", "TEXT NOT NULL DEFAULT ''"),
("forum_threads", "antworten", "INTEGER NOT NULL DEFAULT 0"),
# Forum Sprint 11: erweiterte Thread-Felder
("forum_threads", "foto_urls", "TEXT"),
("forum_threads", "is_pinned", "INTEGER NOT NULL DEFAULT 0"),
("forum_threads", "is_locked", "INTEGER NOT NULL DEFAULT 0"),
("forum_threads", "is_deleted", "INTEGER NOT NULL DEFAULT 0"),
("forum_threads", "likes", "INTEGER NOT NULL DEFAULT 0"),
# Forum Sprint 11: erweiterte Post-Felder
("forum_posts", "foto_urls", "TEXT"),
("forum_posts", "is_deleted", "INTEGER NOT NULL DEFAULT 0"),
("forum_posts", "likes", "INTEGER NOT NULL DEFAULT 0"),
# Users: Moderator-Flag + Forum-Standort
("users", "is_moderator", "INTEGER NOT NULL DEFAULT 0"),
("users", "forum_lat", "REAL"),
("users", "forum_lon", "REAL"),
("users", "forum_show_location", "INTEGER NOT NULL DEFAULT 0"),
# Events: Quelle + externe ID für gescrapte Events
("events", "quelle", "TEXT NOT NULL DEFAULT 'nutzer'"),
("events", "external_id", "TEXT"),
]
with conn_factory() as conn:
for table, column, col_type in migrations:
@ -414,6 +452,142 @@ def _migrate(conn_factory):
)
logger.info(f"Migration: {table}.{column} hinzugefügt.")
# Knigge: Community-Votes
conn.executescript("""
CREATE TABLE IF NOT EXISTS knigge_votes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
szenario_id TEXT NOT NULL,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
answer TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(szenario_id, user_id)
);
""")
# Forum Sprint 11: neue Tabellen
conn.executescript("""
CREATE TABLE IF NOT EXISTS forum_likes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
target_type TEXT NOT NULL,
target_id INTEGER NOT NULL,
UNIQUE(user_id, target_type, target_id)
);
CREATE TABLE IF NOT EXISTS forum_reports (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
target_type TEXT NOT NULL,
target_id INTEGER NOT NULL,
grund TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
resolved INTEGER NOT NULL DEFAULT 0
);
""")
# Wiki: Community-Berichte
conn.executescript("""
CREATE TABLE IF NOT EXISTS wiki_berichte (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
rasse TEXT NOT NULL,
titel TEXT NOT NULL,
text TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_wiki_berichte_rasse ON wiki_berichte(rasse, created_at DESC);
""")
# Hunde-Filme: Bewertungen + Hund des Monats
conn.executescript("""
CREATE TABLE IF NOT EXISTS movie_votes (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
film_id TEXT NOT NULL,
bewertung INTEGER NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(user_id, film_id)
);
CREATE TABLE IF NOT EXISTS hund_des_monats_votes (
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,
monat TEXT NOT NULL,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(user_id, monat)
);
""")
# Events: Unique-Index für externe IDs (idempotent)
conn.executescript("""
CREATE UNIQUE INDEX IF NOT EXISTS idx_events_external
ON events(external_id) WHERE external_id IS NOT NULL;
""")
# Freundschaften + Direktnachrichten
conn.executescript("""
CREATE TABLE IF NOT EXISTS friendships (
id INTEGER PRIMARY KEY AUTOINCREMENT,
requester_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
addressee_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
status TEXT NOT NULL DEFAULT 'pending',
created_at TEXT NOT NULL DEFAULT (datetime('now')),
updated_at TEXT,
UNIQUE(requester_id, addressee_id)
);
CREATE INDEX IF NOT EXISTS idx_friendships_addressee ON friendships(addressee_id, status);
CREATE INDEX IF NOT EXISTS idx_friendships_requester ON friendships(requester_id, status);
CREATE TABLE IF NOT EXISTS conversations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_a_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
user_b_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
last_msg_at TEXT,
a_read_at TEXT,
b_read_at TEXT,
created_at TEXT NOT NULL DEFAULT (datetime('now')),
UNIQUE(user_a_id, user_b_id)
);
CREATE TABLE IF NOT EXISTS direct_messages (
id INTEGER PRIMARY KEY AUTOINCREMENT,
conversation_id INTEGER NOT NULL REFERENCES conversations(id) ON DELETE CASCADE,
sender_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
text TEXT NOT NULL,
is_deleted INTEGER NOT NULL DEFAULT 0,
created_at TEXT NOT NULL DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_dm_conv ON direct_messages(conversation_id, created_at ASC);
""")
# Wiki: Rassen-Datenbank (TheDogAPI)
conn.executescript("""
CREATE TABLE IF NOT EXISTS wiki_rassen (
id INTEGER PRIMARY KEY AUTOINCREMENT,
external_id INTEGER UNIQUE,
name TEXT NOT NULL,
name_de TEXT,
gruppe TEXT,
herkunft TEXT,
temperament TEXT,
gewicht_min_kg REAL,
gewicht_max_kg REAL,
groesse TEXT,
lebensdauer TEXT,
foto_url TEXT,
bred_for TEXT,
aktivitaet TEXT,
wohnung_geeignet INTEGER DEFAULT 0,
kinder_geeignet INTEGER DEFAULT 1,
erfahrung TEXT DEFAULT 'anfaenger',
slug TEXT UNIQUE,
created_at TEXT DEFAULT (datetime('now'))
);
CREATE INDEX IF NOT EXISTS idx_wiki_rassen_slug ON wiki_rassen(slug);
CREATE INDEX IF NOT EXISTS idx_wiki_rassen_gruppe ON wiki_rassen(gruppe);
""")
# Datenmigration: diary_dogs für bestehende Einträge befüllen
conn.execute("""
INSERT OR IGNORE INTO diary_dogs (diary_id, dog_id)