elmeg-demo/backend/routers/social.py
2025-12-26 01:00:32 -08:00

230 lines
7.7 KiB
Python

import re
from typing import List, Optional
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session, select, func
from database import get_session
from models import Comment, Rating, User, Profile, Reaction
from schemas import CommentCreate, CommentRead, RatingCreate, RatingRead, ReactionCreate, ReactionRead
from auth import get_current_user
from helpers import create_notification
from services.gamification import award_xp, check_and_award_badges, update_streak, XP_REWARDS
router = APIRouter(prefix="/social", tags=["social"])
# --- Comments ---
@router.post("/comments", response_model=CommentRead)
def create_comment(
comment: CommentCreate,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
db_comment = Comment.model_validate(comment)
db_comment.user_id = current_user.id
session.add(db_comment)
session.commit()
session.refresh(db_comment)
# Notify parent author if reply
if db_comment.parent_id:
parent_comment = session.get(Comment, db_comment.parent_id)
if parent_comment and parent_comment.user_id != current_user.id:
create_notification(
session,
user_id=parent_comment.user_id,
title="New Reply",
message=f"Someone replied to your comment.",
type="reply",
link=f"/activity"
)
# Handle Mentions
mention_pattern = r"@(\w+)"
mentions = re.findall(mention_pattern, db_comment.content)
if mentions:
# Find users with these profile usernames
mentioned_profiles = session.exec(select(Profile).where(Profile.username.in_(mentions))).all()
for profile in mentioned_profiles:
if profile.user_id != current_user.id:
create_notification(
session,
user_id=profile.user_id,
title="You were mentioned!",
message=f"Someone mentioned you in a comment.",
type="mention",
link=f"/activity" # Generic link for now
)
return db_comment
@router.get("/comments", response_model=List[CommentRead])
def read_comments(
show_id: Optional[int] = None,
venue_id: Optional[int] = None,
song_id: Optional[int] = None,
offset: int = 0,
limit: int = Query(default=50, le=100),
session: Session = Depends(get_session)
):
query = select(Comment)
if show_id:
query = query.where(Comment.show_id == show_id)
if venue_id:
query = query.where(Comment.venue_id == venue_id)
if song_id:
query = query.where(Comment.song_id == song_id)
query = query.order_by(Comment.created_at.desc()).offset(offset).limit(limit)
comments = session.exec(query).all()
return comments
# --- Ratings ---
@router.post("/ratings", response_model=RatingRead)
def create_rating(
rating: RatingCreate,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
# Check if user already rated this entity
query = select(Rating).where(Rating.user_id == current_user.id)
if rating.show_id:
query = query.where(Rating.show_id == rating.show_id)
elif rating.song_id:
query = query.where(Rating.song_id == rating.song_id)
elif rating.performance_id:
query = query.where(Rating.performance_id == rating.performance_id)
elif rating.venue_id:
query = query.where(Rating.venue_id == rating.venue_id)
elif rating.tour_id:
query = query.where(Rating.tour_id == rating.tour_id)
else:
raise HTTPException(status_code=400, detail="Must rate a show, song, performance, venue, or tour")
existing_rating = session.exec(query).first()
if existing_rating:
# Update existing (no XP for updating)
existing_rating.score = rating.score
session.add(existing_rating)
session.commit()
session.refresh(existing_rating)
return existing_rating
# Create new rating with user_id injected
rating_data = rating.model_dump()
rating_data["user_id"] = current_user.id
db_rating = Rating.model_validate(rating_data)
session.add(db_rating)
# Award XP for new rating
# Check if first rating for bonus
rating_count = session.exec(
select(func.count(Rating.id)).where(Rating.user_id == current_user.id)
).one() or 0
xp_amount = XP_REWARDS["rating_submit"]
if rating_count == 0:
xp_amount += XP_REWARDS["first_rating"] # Bonus for first rating
award_xp(session, current_user, xp_amount, "rating")
update_streak(session, current_user)
check_and_award_badges(session, current_user)
session.commit()
session.refresh(db_rating)
return db_rating
@router.get("/ratings/average", response_model=float)
def get_average_rating(
show_id: Optional[int] = None,
song_id: Optional[int] = None,
performance_id: Optional[int] = None,
venue_id: Optional[int] = None,
tour_id: Optional[int] = None,
session: Session = Depends(get_session)
):
query = select(func.avg(Rating.score))
if show_id:
query = query.where(Rating.show_id == show_id)
elif song_id:
query = query.where(Rating.song_id == song_id)
elif performance_id:
query = query.where(Rating.performance_id == performance_id)
elif venue_id:
query = query.where(Rating.venue_id == venue_id)
elif tour_id:
query = query.where(Rating.tour_id == tour_id)
else:
# Return 0 if no entity specified instead of error (graceful degradation)
return 0.0
avg = session.exec(query).first()
return float(avg) if avg else 0.0
# --- Reactions ---
@router.post("/reactions", response_model=ReactionRead)
def toggle_reaction(
reaction: ReactionCreate,
session: Session = Depends(get_session),
current_user: User = Depends(get_current_user)
):
query = select(Reaction).where(
Reaction.user_id == current_user.id,
Reaction.entity_type == reaction.entity_type,
Reaction.entity_id == reaction.entity_id
)
existing = session.exec(query).first()
if existing:
if existing.emoji == reaction.emoji:
# Toggle off
session.delete(existing)
session.commit()
return existing
else:
# Change emoji
existing.emoji = reaction.emoji
session.add(existing)
session.commit()
session.refresh(existing)
return existing
# Create new
db_reaction = Reaction.model_validate(reaction)
db_reaction.user_id = current_user.id
session.add(db_reaction)
session.commit()
session.refresh(db_reaction)
return db_reaction
@router.get("/reactions/counts")
def get_reaction_counts(
entity_type: str,
entity_id: int,
session: Session = Depends(get_session)
):
# Group by emoji and count
query = select(Reaction.emoji, func.count(Reaction.id)).where(
Reaction.entity_type == entity_type,
Reaction.entity_id == entity_id
).group_by(Reaction.emoji)
results = session.exec(query).all()
# returns list of (emoji, count)
return {emoji: count for emoji, count in results}
@router.get("/reactions/me")
def get_my_reaction(
entity_type: str,
entity_id: int,
current_user: User = Depends(get_current_user),
session: Session = Depends(get_session)
):
query = select(Reaction).where(
Reaction.user_id == current_user.id,
Reaction.entity_type == entity_type,
Reaction.entity_id == entity_id
)
return session.exec(query).first()