Files
spotify-cantina/app/routers/voting.py
T
2026-04-23 00:39:58 -04:00

118 lines
3.6 KiB
Python

import uuid
from datetime import datetime, timezone, timedelta
from fastapi import APIRouter, Depends, HTTPException, Request
from sqlalchemy.orm import Session
from app.database import get_db
from app.models import Playlist, Vote, VotingConfig
router = APIRouter(prefix="/voting", tags=["voting"])
COOLDOWN_SECONDS = 10
def _voter_token(request: Request) -> str:
token = request.session.get("voter_token")
if not token:
token = str(uuid.uuid4())
request.session["voter_token"] = token
return token
def _is_open(config: VotingConfig | None) -> bool:
if not config or not config.is_active:
return False
now = datetime.now().strftime("%H:%M")
return config.start_time <= now <= config.end_time
def _cooldown_remaining(db: Session, voter_token: str) -> int:
"""Segundos restantes de cooldown. 0 si puede votar."""
last = (
db.query(Vote)
.filter(Vote.voter_token == voter_token)
.order_by(Vote.voted_at.desc())
.first()
)
if not last:
return 0
voted_at = last.voted_at
if voted_at.tzinfo is None:
voted_at = voted_at.replace(tzinfo=timezone.utc)
elapsed = (datetime.now(timezone.utc) - voted_at).total_seconds()
return max(0, int(COOLDOWN_SECONDS - elapsed))
@router.get("/status")
def voting_status(request: Request, db: Session = Depends(get_db)):
config = db.query(VotingConfig).first()
voter_token = _voter_token(request)
is_open = _is_open(config)
playlists = db.query(Playlist).order_by(Playlist.created_at.desc()).all()
vote_counts: dict[int, int] = {
pl.id: db.query(Vote).filter(Vote.playlist_id == pl.id).count()
for pl in playlists
}
last_vote = (
db.query(Vote)
.filter(Vote.voter_token == voter_token)
.order_by(Vote.voted_at.desc())
.first()
)
return {
"is_open": is_open,
"cooldown_seconds": COOLDOWN_SECONDS,
"cooldown_remaining": _cooldown_remaining(db, voter_token),
"config": {
"start_time": config.start_time if config else None,
"end_time": config.end_time if config else None,
"is_active": config.is_active if config else False,
},
"my_last_vote_playlist_id": last_vote.playlist_id if last_vote else None,
"playlists": [
{
"id": pl.id,
"spotify_id": pl.spotify_id,
"spotify_type": pl.spotify_type,
"name": pl.name,
"image_url": pl.image_url,
"emoji": pl.emoji or "",
"description": pl.description or "",
"votes": vote_counts.get(pl.id, 0),
}
for pl in playlists
],
}
@router.post("/vote/{playlist_id}")
def cast_vote(playlist_id: int, request: Request, db: Session = Depends(get_db)):
config = db.query(VotingConfig).first()
if not _is_open(config):
raise HTTPException(status_code=403, detail="La votación no está abierta en este momento")
pl = db.query(Playlist).filter(Playlist.id == playlist_id).first()
if not pl:
raise HTTPException(status_code=404, detail="Playlist no encontrada")
voter_token = _voter_token(request)
remaining = _cooldown_remaining(db, voter_token)
if remaining > 0:
raise HTTPException(
status_code=429,
detail={"message": "Debes esperar antes de votar de nuevo", "remaining": remaining},
)
db.add(Vote(
playlist_id=playlist_id,
voter_token=voter_token,
voted_at=datetime.now(timezone.utc),
))
db.commit()
return {"ok": True, "cooldown_seconds": COOLDOWN_SECONDS}