Files
boxofficefantasy/boxofficefantasy/views.py
Anthony Correa c9ce7a36d0 Integrate draft session support with phase handling and real-time updates
- Added user authentication UI in the base template for navbar.
- Expanded `league.dj.html` to include a new "Draft Sessions" tab showing active drafts.
- Refactored Django views and models to support `DraftSession` with participants and movies.
- Replaced deprecated models like `DraftParticipant` and `DraftMoviePool` with a new schema using `DraftSessionParticipant`.
- Introduced WebSocket consumers (`DraftAdminConsumer`, `DraftParticipantConsumer`) with structured phase logic and caching.
- Added `DraftStateManager` for managing draft state in Django cache.
- Created frontend UI components in React for draft admin and participants, including phase control and WebSocket message logging.
- Updated SCSS styles for improved UI structure and messaging area.
2025-08-02 08:56:41 -05:00

366 lines
12 KiB
Python
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from django.shortcuts import render, get_object_or_404
from django.contrib.auth import get_user_model
from django.http.response import Http404, HttpResponse
from django.urls import reverse
from django.db.models import OuterRef, Subquery, Sum, Q
from boxofficefantasy.models import League, Season, UserSeasonEntry, Movie, Pick
from draft.models import DraftSession
from .integrations.tmdb import get_tmdb_movie_by_imdb, cache_tmdb_poster
User = get_user_model()
def parse_season_slug(season_slug: str) -> tuple[str, str]:
try:
label, year = season_slug.rsplit("-", 1)
year = int(year)
return (label, year)
except ValueError:
raise Http404("Invalid season format.")
def get_scoreboard(user_season_entries=list[UserSeasonEntry]):
scoreboard = []
for season_entry in user_season_entries:
picks = Pick.objects.filter(season_entry=season_entry).select_related("movie")
total_score = 0
pick_data = []
for pick in picks:
movie = pick.movie
metrics = {m.key: m.value for m in movie.moviemetric_set.all()}
score = metrics.get("domestic_gross", 0)
total_score += score
pick_data.append(
{
"imdb_id": movie.imdb_id,
"movie": movie.title,
"score": score,
"bid": pick.bid_amount,
}
)
pick_data.sort(key=lambda e: e["score"], reverse=True)
scoreboard.append(
{
"team": season_entry.team_name,
"user": season_entry.user.username,
"total": total_score,
"picks": pick_data,
}
)
scoreboard.sort(key=lambda e: e["total"], reverse=True)
return scoreboard
# Create your views here.
def scoreboard_view(request, league_slug, season_slug):
# season_slug is something like "summer-2025"
season_label, season_year = parse_season_slug(season_slug)
league = get_object_or_404(League, slug=league_slug)
season = get_object_or_404(
Season, league=league, year=season_year, label__iexact=season_label
)
entries = UserSeasonEntry.objects.filter(season=season).select_related("user")
scoreboard = get_scoreboard(entries)
return render(
request,
"scoreboard.dj.html",
{
"league": league,
"season": season,
"scoreboard": scoreboard,
"breadcrumbs": [
{"label": league.name, "url": "#"},
{
"label": "seasons",
"url": reverse("league:seasons", args=[league.slug]),
},
{"label": f"{season.label} {season.year}", "url": "#"},
],
},
)
def team_view(request, league_slug=None, season_slug=None, username=None):
if not league_slug:
return HttpResponse(
"Team View: League not specified", content_type="text/plain"
)
league = get_object_or_404(League, slug=league_slug)
# 1⃣ League only all teams across all seasons in the league
if not season_slug:
entries = UserSeasonEntry.objects.filter(season__league=league).select_related(
"user", "season"
)
return render(
request,
"teams.dj.html",
{
"entries": entries,
"league": league,
},
)
# 2⃣ League + Season all teams in that season
season_label, season_year = parse_season_slug(season_slug)
season = get_object_or_404(
Season, league=league, year=season_year, label__iexact=season_label
)
if not username:
entries = UserSeasonEntry.objects.filter(season=season).select_related("user")
return render(
request,
"teams.dj.html",
{
"user_season_entries": [
{
"name": user_season_entry.user.get_full_name(),
"team_name": user_season_entry.team_name,
"username": user_season_entry.user.username,
}
for user_season_entry in entries
],
"league": {"name": league.name},
"season": {"label": season.label, "year": season.year},
},
)
# 3⃣ League + Season + Username one team and its picks
user = get_object_or_404(User, username=username)
entry = get_object_or_404(UserSeasonEntry, season=season, user=user)
picks = (
Pick.objects.filter(season_entry=entry)
.select_related("movie")
.prefetch_related("movie__moviemetric_set")
)
movie_data = []
for pick in picks:
metrics = {m.key: m.value for m in pick.movie.moviemetric_set.all()}
movie_data.append(
{
"movie": pick.movie,
"bid": pick.bid_amount,
"score": metrics.get("domestic_gross", 0),
}
)
return render(
request,
"team_detail.dj.html",
{
"entry": entry,
"picks": movie_data,
"league": league,
"season": season,
"user": user,
},
)
def movie_view(request, league_slug=None, season_slug=None, imdb_id=None):
if not league_slug:
return HttpResponse("Movie View: No league provided", content_type="text/plain")
# 1⃣ League only — all movies across seasons
if league_slug and not season_slug and not imdb_id:
league = get_object_or_404(League, slug=league_slug)
picks = Pick.objects.filter(season__league=league)
movie_data = [
{
"title": pick.movie.title,
"score": (
pick.movie.moviemetric_set.filter(key="domestic_gross")
.first()
.value
if pick.movie
else 0
),
"bid": pick.bid_amount,
"team_name": pick.season_entry.team_name,
"user": pick.season_entry.user.username,
}
for pick in picks
]
return render(
request, "movies.dj.html", {"movies": movie_data, "league": league}
)
# 2⃣ League + Season — all movies in that season
if league_slug and season_slug and not imdb_id:
season_label, season_year = parse_season_slug(season_slug)
league = get_object_or_404(League, slug=league_slug)
season = get_object_or_404(
Season, league=league, year=season_year, label__iexact=season_label
)
picks = season.pick_set.select_related(
"movie", "season_entry", "season_entry__user"
)
movie_data = [
{
"id": pick.movie.bom_legacy_id,
"title": pick.movie.title,
"score": (
pick.movie.moviemetric_set.filter(key="domestic_gross")
.first()
.value
if pick.movie
else 0
),
"bid": pick.bid_amount,
"team_name": pick.season_entry.team_name,
"user": pick.season_entry.user.username,
}
for pick in picks
]
return render(
request,
"movies.dj.html",
{"movies": movie_data, "season": season, "league": league},
)
# 3⃣ League + Season + Movie — show movie details
if league_slug and season_slug and imdb_id:
season_label, season_year = parse_season_slug(season_slug)
league = get_object_or_404(League, slug=league_slug)
season = get_object_or_404(
Season, league=league, year=season_year, label__iexact=season_label
)
movie = get_object_or_404(Movie, imdb_id=imdb_id)
picks = movie.pick_set.filter(season=season).select_related(
"season_entry", "season_entry__user"
)
metrics = {m.key: m.value for m in movie.moviemetric_set.all()}
tmdb_data = get_tmdb_movie_by_imdb(movie.imdb_id)
data = {
"movie": movie,
"tmdb_data": tmdb_data,
"poster_path": cache_tmdb_poster(tmdb_data.poster_path),
"metrics": metrics,
"picks": picks,
"season": season,
"league": league,
}
return render(request, "movie.dj.html", data)
return HttpResponse("Invalid parameter combination.", content_type="text/plain")
def season_view(request, league_slug, season_slug=None):
if not league_slug:
return HttpResponse("League not specified", content_type="text/plain")
league = get_object_or_404(League, slug=league_slug)
# 1⃣ League only list all seasons in the league
if not season_slug:
seasons = [
{
"label": season.label,
"year": season.year,
"slug": season.slug,
"league": {"name": season.league.name, "slug": season.league.slug},
}
for season in league.season_set.all()
]
return render(
request,
"seasons.dj.html",
{
"seasons": seasons,
"league": league,
},
)
# 2⃣ League + season show a basic detail page or placeholder
season_label, season_year = parse_season_slug(season_slug)
season = get_object_or_404(
Season, league=league, year=season_year, label__iexact=season_label
)
entries = UserSeasonEntry.objects.filter(season=season).select_related("user")
picks = season.pick_set.select_related(
"season_entry", "season_entry__user"
).annotate(
domestic_gross=Sum(
"movie__moviemetric__value",
filter=Q(movie__moviemetric__key="domestic_gross"),
)
)
return render(
request,
"season.dj.html",
{
"season": season,
"league": league,
"scoreboard": get_scoreboard(entries),
"picks": picks,
},
)
def league_view(request, league_slug=None):
# 1⃣ League only list all seasons in the league
if not league_slug:
return render(request, "leagues.dj.html", {"leagues": League.objects.all()})
league = get_object_or_404(League, slug=league_slug)
# Subquery: top entry per season by total score
top_entry = (
UserSeasonEntry.objects.filter(season=OuterRef("pk"))
.annotate(
total_score=Sum(
"pick__movie__moviemetric__value",
filter=Q(pick__movie__moviemetric__key="domestic_gross"),
)
)
.order_by("-total_score")
)
winner_team = top_entry.values("team_name")[:1]
winner_score = top_entry.values("total_score")[:1]
# Subquery: pick with top-grossing movie for the season
top_pick = (
Pick.objects.filter(season=OuterRef("pk"))
.annotate(
gross=Sum(
"movie__moviemetric__value",
filter=Q(movie__moviemetric__key="domestic_gross"),
)
)
.order_by("-gross")
)
top_movie = top_pick.values("movie__title")[:1]
top_gross = top_pick.values("gross")[:1]
seasons = Season.objects.annotate(
winner_team=Subquery(winner_team),
winner_score=Subquery(winner_score),
top_movie=Subquery(top_movie),
top_movie_score=Subquery(top_gross),
).order_by("-year")
draft_sessions = DraftSession.objects.filter(season__league=league)
return render(
request,
"league.dj.html",
{
"league": league,
"seasons": seasons,
"draft_sessions": draft_sessions
},
)