goon/alembic/versions/20261002_0052_clips_feed_counters.py
goon-foss 4666fcb04a feat(edge): nowa punktacja feedu z historii klipu u nas, 25% w grupie kontrolnej
Odtworzenie uczenia na 189 tys. zdarzen (wagi zdarzenie po zdarzeniu, bez
zagladania w przyszlosc, test na 30% najnowszych): stara punktacja porzadkowala
klipy w obrebie urzadzenia z AUC 0,576 (lajk albo >= 10 s vs ucieczka < 2 s).
Tagi RedGifs sa liczne i ogolne, wiec ich suma dominowala, a mowila malo; nisze
prawie nic, popularnosc z RedGifs slabo, tworca i przyjecie klipu u nas najwiecej.

Nowa: log(waga tagu x rzadkosc tagu) + 0,25 log(nisze) + 2,5 log(tworca)
+ 0,8 ln(1+lajki RedGifs) + 120 (odsetek lajkow u nas - 3%) - 15 (odsetek
ucieczek - 50%) + 0,3 za swiezosc. Na tescie 0,652, regresja na tych samych
cechach 0,654. Liczniki clips.feed_views/skips/likes (migracja 0052, wypelnione
z historii), bumpowane w POST /clips/events. 25% urzadzen (hash id) zostaje na
starej punktacji do porownania na zywym ruchu.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 11:47:02 +02:00

55 lines
2.1 KiB
Python

"""Liczniki klipu z feedu Edge: wyswietlenia, ucieczki (< 2 s) i lajki w samym Goonie.
Odtworzenie uczenia feedu na 189 tys. zdarzen (2026-10-02) pokazalo, ze najmocniejszym
sygnalem tego, czy klip sie spodoba, jest to, jak przyjeli go inni u nas: odsetek lajkow
(AUC w obrebie urzadzenia 0,600) bije wagi tagow (0,571), a popularnosc z RedGifs prawie
nic nie mowi (0,532). Punktacja (app/clips_feed.py) potrzebuje tych liczb w chwili
ukladania strony, stad kolumny przy klipie zamiast agregatu po clip_feed_events.
ADD COLUMN ze stalym DEFAULT nie przepisuje tabeli (Postgres 11+). Wypelnienie z
dotychczasowych zdarzen jednym UPDATE (~70 tys. klipow).
Revision ID: 0052_clips_feed_counters
Revises: 0051_movies_tpdb_checked_at
Create Date: 2026-10-02
"""
from __future__ import annotations
from alembic import op
revision = "0052_clips_feed_counters"
down_revision: str | None = "0051_movies_tpdb_checked_at"
branch_labels = None
depends_on = None
def upgrade() -> None:
op.execute("SET LOCAL lock_timeout = '5s'")
op.execute("""
ALTER TABLE clips
ADD COLUMN IF NOT EXISTS feed_views integer NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS feed_skips integer NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS feed_likes integer NOT NULL DEFAULT 0
""")
op.execute("""
UPDATE clips c SET feed_views = a.v, feed_skips = a.s, feed_likes = greatest(a.l, 0)
FROM (
SELECT clip_id,
count(*) FILTER (WHERE kind IN ('complete', 'skip', 'impression', 'autoswipe')) AS v,
count(*) FILTER (WHERE kind = 'skip') AS s,
count(*) FILTER (WHERE kind = 'like') - count(*) FILTER (WHERE kind = 'unlike') AS l
FROM clip_feed_events WHERE clip_id IS NOT NULL
GROUP BY clip_id
) a
WHERE a.clip_id = c.id
""")
def downgrade() -> None:
op.execute("SET LOCAL lock_timeout = '5s'")
op.execute("""
ALTER TABLE clips
DROP COLUMN IF EXISTS feed_views,
DROP COLUMN IF EXISTS feed_skips,
DROP COLUMN IF EXISTS feed_likes
""")