feat: Add personalized feed endpoint with vertical filtering
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s

- Add GET /feed/me endpoint
- Filters reviews and attendance by user's band preferences
- Excludes 'hidden' display_mode bands
- Falls back to all bands if no preferences set
This commit is contained in:
fullsizemalt 2025-12-28 16:05:43 -08:00
parent c1c041bbe9
commit 5ced96f4e6

View file

@ -5,6 +5,7 @@ from database import get_session
from models import Review, Attendance, GroupPost, User, Profile, Performance, Show, Song from models import Review, Attendance, GroupPost, User, Profile, Performance, Show, Song
from schemas import ReviewRead, AttendanceRead, GroupPostRead from schemas import ReviewRead, AttendanceRead, GroupPostRead
from datetime import datetime from datetime import datetime
from auth import get_current_user_optional
router = APIRouter(prefix="/feed", tags=["feed"]) router = APIRouter(prefix="/feed", tags=["feed"])
@ -129,3 +130,92 @@ def get_global_feed(
feed_items.sort(key=lambda x: x.timestamp, reverse=True) feed_items.sort(key=lambda x: x.timestamp, reverse=True)
return feed_items[:limit] return feed_items[:limit]
@router.get("/me", response_model=List[FeedItem])
def get_personalized_feed(
limit: int = 20,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user_optional)
):
"""Get feed filtered by user's band preferences (or all bands if not logged in)"""
from models import UserVerticalPreference, Vertical
# Get user's preferred vertical IDs
preferred_vertical_ids = None
if current_user:
prefs = session.exec(
select(UserVerticalPreference)
.where(UserVerticalPreference.user_id == current_user.id)
.where(UserVerticalPreference.display_mode != "hidden")
).all()
if prefs:
preferred_vertical_ids = [p.vertical_id for p in prefs]
# Fetch reviews (filtered by vertical if user has preferences)
review_query = select(Review).order_by(desc(Review.created_at)).limit(limit * 2)
reviews = session.exec(review_query).all()
# Fetch attendance (filtered by show's vertical)
attendance_query = select(Attendance).order_by(desc(Attendance.created_at)).limit(limit * 2)
attendance = session.exec(attendance_query).all()
feed_items = []
for r in reviews:
# Filter by vertical if user has preferences
vertical_id = None
if r.performance_id:
perf = session.get(Performance, r.performance_id)
if perf:
show = session.get(Show, perf.show_id)
if show:
vertical_id = show.vertical_id
elif r.show_id:
show = session.get(Show, r.show_id)
if show:
vertical_id = show.vertical_id
elif r.song_id:
song = session.get(Song, r.song_id)
if song:
vertical_id = song.vertical_id
if preferred_vertical_ids and vertical_id and vertical_id not in preferred_vertical_ids:
continue
feed_items.append(FeedItem(
type="review",
timestamp=r.created_at or datetime.utcnow(),
data=r,
user=get_user_display(session, r.user_id),
entity=get_entity_info(session, r)
))
for a in attendance:
show = session.get(Show, a.show_id) if a.show_id else None
# Filter by vertical
if preferred_vertical_ids and show and show.vertical_id not in preferred_vertical_ids:
continue
entity_info = None
if show:
entity_info = {
"type": "show",
"slug": show.slug,
"title": show.date.strftime("%Y-%m-%d") if show.date else "Unknown",
}
feed_items.append(FeedItem(
type="attendance",
timestamp=a.created_at,
data=a,
user=get_user_display(session, a.user_id),
entity=entity_info
))
# Sort by timestamp desc
feed_items.sort(key=lambda x: x.timestamp, reverse=True)
return feed_items[:limit]