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
221 lines
7.3 KiB
Python
221 lines
7.3 KiB
Python
from typing import List, Union
|
|
from fastapi import APIRouter, Depends, Query
|
|
from sqlmodel import Session, select, desc
|
|
from database import get_session
|
|
from models import Review, Attendance, GroupPost, User, Profile, Performance, Show, Song
|
|
from schemas import ReviewRead, AttendanceRead, GroupPostRead
|
|
from datetime import datetime
|
|
from auth import get_current_user_optional
|
|
|
|
router = APIRouter(prefix="/feed", tags=["feed"])
|
|
|
|
# We need a unified schema for the feed
|
|
from pydantic import BaseModel
|
|
|
|
class FeedItem(BaseModel):
|
|
type: str # review, attendance, post
|
|
timestamp: datetime
|
|
data: Union[ReviewRead, AttendanceRead, GroupPostRead, dict]
|
|
user: dict # Basic user info
|
|
entity: dict | None = None # Linked entity info
|
|
|
|
def get_user_display(session: Session, user_id: int) -> dict:
|
|
"""Get consistent user display info using Profile username"""
|
|
user = session.get(User, user_id)
|
|
if not user:
|
|
return {"id": 0, "username": "Deleted User", "avatar_bg_color": "#666", "avatar_text": None}
|
|
|
|
profile = session.exec(
|
|
select(Profile).where(Profile.user_id == user_id)
|
|
).first()
|
|
|
|
return {
|
|
"id": user.id,
|
|
"username": profile.username if profile else f"User {user_id}",
|
|
"display_name": profile.display_name if profile else None,
|
|
"avatar_bg_color": user.avatar_bg_color or "#0F4C81",
|
|
"avatar_text": user.avatar_text,
|
|
}
|
|
|
|
def get_entity_info(session: Session, review: Review) -> dict | None:
|
|
"""Get entity link info for a review"""
|
|
if review.performance_id:
|
|
perf = session.get(Performance, review.performance_id)
|
|
if perf:
|
|
song = session.get(Song, perf.song_id)
|
|
show = session.get(Show, perf.show_id)
|
|
return {
|
|
"type": "performance",
|
|
"slug": perf.slug,
|
|
"title": song.title if song else "Unknown Song",
|
|
"date": show.date.isoformat() if show and show.date else None,
|
|
}
|
|
elif review.show_id:
|
|
show = session.get(Show, review.show_id)
|
|
if show:
|
|
return {
|
|
"type": "show",
|
|
"slug": show.slug,
|
|
"title": show.date.strftime("%Y-%m-%d") if show.date else "Unknown Date",
|
|
}
|
|
elif review.song_id:
|
|
song = session.get(Song, review.song_id)
|
|
if song:
|
|
return {
|
|
"type": "song",
|
|
"slug": song.slug,
|
|
"title": song.title,
|
|
}
|
|
return None
|
|
|
|
@router.get("/", response_model=List[FeedItem])
|
|
def get_global_feed(
|
|
limit: int = 20,
|
|
session: Session = Depends(get_session)
|
|
):
|
|
# Fetch latest reviews
|
|
reviews = session.exec(
|
|
select(Review).order_by(desc(Review.created_at)).limit(limit)
|
|
).all()
|
|
|
|
# Fetch latest attendance
|
|
attendance = session.exec(
|
|
select(Attendance).order_by(desc(Attendance.created_at)).limit(limit)
|
|
).all()
|
|
|
|
# Fetch latest group posts
|
|
posts = session.exec(
|
|
select(GroupPost).order_by(desc(GroupPost.created_at)).limit(limit)
|
|
).all()
|
|
|
|
feed_items = []
|
|
|
|
for r in reviews:
|
|
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
|
|
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
|
|
))
|
|
|
|
for p in posts:
|
|
feed_items.append(FeedItem(
|
|
type="post",
|
|
timestamp=p.created_at,
|
|
data=p,
|
|
user=get_user_display(session, p.user_id),
|
|
entity=None
|
|
))
|
|
|
|
# Sort by timestamp desc
|
|
feed_items.sort(key=lambda x: x.timestamp, reverse=True)
|
|
|
|
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]
|
|
|