feat(social): Add Reaction model, schema, and API endpoints
Some checks are pending
Deploy Elmeg / deploy (push) Waiting to run
Some checks are pending
Deploy Elmeg / deploy (push) Waiting to run
This commit is contained in:
parent
a4edbb676d
commit
d5b5ee8192
3 changed files with 94 additions and 2 deletions
|
|
@ -279,3 +279,13 @@ class Notification(SQLModel, table=True):
|
||||||
created_at: datetime = Field(default_factory=datetime.utcnow)
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
|
||||||
user: User = Relationship(back_populates="notifications")
|
user: User = Relationship(back_populates="notifications")
|
||||||
|
|
||||||
|
class Reaction(SQLModel, table=True):
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
user_id: int = Field(foreign_key="user.id")
|
||||||
|
entity_type: str = Field(index=True) # "review", "comment"
|
||||||
|
entity_id: int = Field(index=True)
|
||||||
|
emoji: str # "❤️", "🔥", etc.
|
||||||
|
created_at: datetime = Field(default_factory=datetime.utcnow)
|
||||||
|
|
||||||
|
user: User = Relationship()
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@ from typing import List, Optional
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from sqlmodel import Session, select, func
|
from sqlmodel import Session, select, func
|
||||||
from database import get_session
|
from database import get_session
|
||||||
from models import Comment, Rating, User, Profile
|
from models import Comment, Rating, User, Profile, Reaction
|
||||||
from schemas import CommentCreate, CommentRead, RatingCreate, RatingRead
|
from schemas import CommentCreate, CommentRead, RatingCreate, RatingRead, ReactionCreate, ReactionRead
|
||||||
from auth import get_current_user
|
from auth import get_current_user
|
||||||
from helpers import create_notification
|
from helpers import create_notification
|
||||||
|
|
||||||
|
|
@ -121,3 +121,71 @@ def get_average_rating(
|
||||||
|
|
||||||
avg = session.exec(query).first()
|
avg = session.exec(query).first()
|
||||||
return float(avg) if avg else 0.0
|
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()
|
||||||
|
|
|
||||||
|
|
@ -346,3 +346,17 @@ class TagRead(TagBase):
|
||||||
# Circular refs
|
# Circular refs
|
||||||
ShowRead.model_rebuild()
|
ShowRead.model_rebuild()
|
||||||
PerformanceDetailRead.model_rebuild()
|
PerformanceDetailRead.model_rebuild()
|
||||||
|
|
||||||
|
# --- Reaction Schemas ---
|
||||||
|
class ReactionBase(SQLModel):
|
||||||
|
entity_type: str
|
||||||
|
entity_id: int
|
||||||
|
emoji: str
|
||||||
|
|
||||||
|
class ReactionCreate(ReactionBase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class ReactionRead(ReactionBase):
|
||||||
|
id: int
|
||||||
|
user_id: int
|
||||||
|
created_at: datetime
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue