Add DRF API app and real-time draft management UI
- Created new `api` Django app with serializers, viewsets, and routers to expose draft sessions, participants, and movie data. - Registered `api` app in settings and updated root URL configuration. - Extended WebSocket consumers with `inform.draft_status` / `request.draft_status` to allow fetching current draft state. - Updated `DraftSession` and related models to support reverse lookups for draft picks. - Enhanced draft state manager to include `draft_order` in summaries. - Added React WebSocket context provider, connection status component, and new admin/participant panels with phase and participant tracking. - Updated SCSS for participant lists, phase indicators, and status badges. - Modified Django templates to mount new React roots for admin and participant views. - Updated Webpack dev server config to proxy WebSocket connections.
This commit is contained in:
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
3
api/admin.py
Normal file
3
api/admin.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
api/apps.py
Normal file
6
api/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'api'
|
||||
0
api/migrations/__init__.py
Normal file
0
api/migrations/__init__.py
Normal file
3
api/models.py
Normal file
3
api/models.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
59
api/serializers.py
Normal file
59
api/serializers.py
Normal file
@@ -0,0 +1,59 @@
|
||||
from rest_framework import serializers
|
||||
from django.contrib.auth import get_user_model
|
||||
from boxofficefantasy.models import Movie, Season
|
||||
from draft.models import DraftSession, DraftSessionSettings, DraftPick
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
class UserSerializer(serializers.ModelSerializer):
|
||||
full_name = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "first_name", "last_name", "email", "full_name")
|
||||
|
||||
def get_full_name(self, obj):
|
||||
return f"{obj.first_name} {obj.last_name}".strip()
|
||||
|
||||
class MovieSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = Movie
|
||||
# fields = ("id", "imdb_id", "title", "year", "poster_url")
|
||||
fields = ("id", "title")
|
||||
|
||||
class DraftSessionSettingsSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = DraftSessionSettings
|
||||
fields = ("starting_budget",) # add any others you have
|
||||
|
||||
|
||||
class DraftPickSerializer(serializers.ModelSerializer):
|
||||
user = UserSerializer(read_only=True)
|
||||
movie = MovieSerializer(read_only=True)
|
||||
|
||||
class Meta:
|
||||
model = DraftPick
|
||||
fields = ("id", "movie", "winner", "bid_amount")
|
||||
|
||||
class DraftSessionSerializer(serializers.ModelSerializer):
|
||||
participants = UserSerializer(many=True, read_only=True)
|
||||
movies = MovieSerializer(many=True, read_only=True)
|
||||
settings = DraftSessionSettingsSerializer(read_only=True)
|
||||
draft_picks = DraftPickSerializer(many=True, read_only=True)
|
||||
|
||||
def hashid(self, obj):
|
||||
return f"{obj.hashid}".strip()
|
||||
|
||||
class Meta:
|
||||
model = DraftSession
|
||||
# include whatever else you want (phase, season info, hashed_id, etc.)
|
||||
fields = (
|
||||
"id",
|
||||
"hashid",
|
||||
"season", # will use __str__ unless you customize
|
||||
"participants",
|
||||
"movies",
|
||||
"settings",
|
||||
"draft_picks",
|
||||
# optionally include server time for client clock sync
|
||||
)
|
||||
3
api/tests.py
Normal file
3
api/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
9
api/urls.py
Normal file
9
api/urls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from .views import UserViewSet, MovieViewSet, DraftSessionViewSet
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'users', UserViewSet, basename='user')
|
||||
router.register(r'movies', MovieViewSet, basename='movie')
|
||||
router.register(r'draft', DraftSessionViewSet, basename='draft')
|
||||
|
||||
urlpatterns = router.urls
|
||||
60
api/views.py
Normal file
60
api/views.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from rest_framework import viewsets, permissions
|
||||
from rest_framework.exceptions import NotFound
|
||||
from django.contrib.auth import get_user_model
|
||||
from boxofficefantasy.models import Movie
|
||||
from draft.models import DraftSession, DraftPick
|
||||
from django.shortcuts import get_object_or_404
|
||||
|
||||
|
||||
from django.db.models import Prefetch
|
||||
|
||||
from .serializers import (
|
||||
UserSerializer, MovieSerializer, DraftSessionSerializer
|
||||
)
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
class UserViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = User.objects.all()
|
||||
serializer_class = UserSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
lookup_field = "username"
|
||||
|
||||
|
||||
class MovieViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
queryset = Movie.objects.all().order_by('id')
|
||||
serializer_class = MovieSerializer
|
||||
permission_classes = [permissions.IsAuthenticated]
|
||||
|
||||
class DraftSessionViewSet(viewsets.ReadOnlyModelViewSet):
|
||||
"""
|
||||
GET /api/drafts/<hashed_id>/
|
||||
Returns participants, movies, settings, and picks for a draft session.
|
||||
Access limited to participants or staff.
|
||||
"""
|
||||
serializer_class = DraftSessionSerializer
|
||||
# permission_classes = [permissions.IsAuthenticated, IsParticipantOfDraft]
|
||||
lookup_field = "hashid" # use hashed id instead of pk
|
||||
lookup_url_kwarg = "hid" # url kwarg name matches urls.py
|
||||
|
||||
def get_object(self):
|
||||
hashid = self.kwargs[self.lookup_url_kwarg]
|
||||
pk = DraftSession.decode_id(hashid)
|
||||
if pk is None:
|
||||
raise NotFound("Invalid draft id.")
|
||||
obj = get_object_or_404(self.get_queryset(), pk=pk)
|
||||
# Trigger object-level permissions (participant check happens here)
|
||||
self.check_object_permissions(self.request, obj)
|
||||
return obj
|
||||
|
||||
def get_queryset(self):
|
||||
# Optimize queries
|
||||
return (
|
||||
DraftSession.objects
|
||||
.select_related("season", "settings")
|
||||
.prefetch_related(
|
||||
"participants",
|
||||
"movies",
|
||||
Prefetch("draft_picks", queryset=DraftPick.objects.select_related("winner", "movie")),
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user