Improve draft UI state handling, layout, and order logic
- Added current/next pick info, updated server draft logic for order/snake - Refactored WebSocketContext, removed dead code, improved CSS/layout - Cleaned up template blocks, admin, and participant panel structure
This commit is contained in:
@@ -40,7 +40,7 @@ class DraftConsumerBase(AsyncJsonWebsocketConsumer):
|
||||
|
||||
self.group_names = DraftGroupChannelNames(draft_hashid)
|
||||
self.cache_keys = DraftCacheKeys(draft_hashid)
|
||||
self.draft_state = DraftStateManager(draft_hashid, self.draft_session.settings)
|
||||
self.draft_state = DraftStateManager(self.draft_session)
|
||||
|
||||
self.user = self.scope["user"]
|
||||
if not self.should_accept_user():
|
||||
@@ -172,7 +172,7 @@ class DraftAdminConsumer(DraftConsumerBase):
|
||||
await self.start_nominate()
|
||||
|
||||
if event_type == DraftMessage.DRAFT_INDEX_ADVANCE_REQUEST:
|
||||
self.draft_state.draft_index += 1
|
||||
self.draft_state.draft_index_advance()
|
||||
await self.channel_layer.group_send(
|
||||
self.group_names.session,
|
||||
{
|
||||
@@ -229,18 +229,22 @@ class DraftAdminConsumer(DraftConsumerBase):
|
||||
)
|
||||
|
||||
async def determine_draft_order(self):
|
||||
draft_order = random.sample(
|
||||
self.draft_participants, len(self.draft_participants)
|
||||
)
|
||||
self.draft_state.draft_order = [p.username for p in draft_order]
|
||||
draft_order = self.draft_state.determine_draft_order(self.draft_participants)
|
||||
self.draft_state.draft_index = 0
|
||||
await self.set_draft_phase(DraftPhase.DETERMINE_ORDER)
|
||||
next_picks = self.draft_state.next_picks(include_current=True)
|
||||
|
||||
await self.channel_layer.group_send(
|
||||
self.group_names.session,
|
||||
{
|
||||
"type": "broadcast.session",
|
||||
"subtype": DraftMessage.ORDER_DETERMINE_CONFIRM,
|
||||
"payload": {"draft_order": self.draft_state.draft_order},
|
||||
"payload": {
|
||||
"draft_order": draft_order,
|
||||
"draft_index": self.draft_state.draft_index,
|
||||
"current_pick": next_picks[0],
|
||||
"next_picks": next_picks[1:]
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -4,8 +4,11 @@ from datetime import datetime, timedelta
|
||||
from boxofficefantasy.models import Movie
|
||||
from django.contrib.auth.models import User
|
||||
from draft.constants import DraftPhase
|
||||
from draft.models import DraftSessionSettings
|
||||
from draft.models import DraftSession
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Dict, List, Literal, Optional, Sequence, Tuple
|
||||
import random
|
||||
|
||||
class DraftCacheKeys:
|
||||
def __init__(self, id):
|
||||
@@ -73,12 +76,12 @@ class DraftCacheKeys:
|
||||
# return f"{self.prefix}:user:{user_id}:channel"
|
||||
|
||||
class DraftStateManager:
|
||||
def __init__(self, session_id: int, settings: DraftSessionSettings):
|
||||
self.session_id = session_id
|
||||
def __init__(self, session: DraftSession):
|
||||
self.session_id = session.hashid
|
||||
self.cache = cache
|
||||
self.keys = DraftCacheKeys(session_id)
|
||||
self.keys = DraftCacheKeys(self.session_id)
|
||||
self._initial_phase = self.cache.get(self.keys.phase, DraftPhase.WAITING.value)
|
||||
self.settings = settings
|
||||
self.settings = session.settings
|
||||
|
||||
# === Phase Management ===
|
||||
@property
|
||||
@@ -114,6 +117,13 @@ class DraftStateManager:
|
||||
if not isinstance(draft_order, list):
|
||||
return
|
||||
self.cache.set(self.keys.draft_order,json.dumps(draft_order))
|
||||
|
||||
def determine_draft_order(self, users: list[User]):
|
||||
draft_order = random.sample(
|
||||
users, len(users)
|
||||
)
|
||||
self.draft_order = [user.username for user in draft_order]
|
||||
return self.draft_order
|
||||
|
||||
@property
|
||||
def draft_index(self):
|
||||
@@ -122,6 +132,42 @@ class DraftStateManager:
|
||||
@draft_index.setter
|
||||
def draft_index(self, draft_index: int):
|
||||
self.cache.set(self.keys.draft_index, int(draft_index))
|
||||
|
||||
def draft_index_advance(self, n: int = 1):
|
||||
self.draft_index += n
|
||||
return self.draft_index
|
||||
|
||||
def next_picks(
|
||||
self,
|
||||
*,
|
||||
from_overall: int | None = None,
|
||||
count: int | None = None,
|
||||
include_current: bool = False,
|
||||
) -> List[dict]:
|
||||
"""
|
||||
Convenience: return the next `count` picks starting after `from_overall`
|
||||
(or after current draft_index if omitted). Each item:
|
||||
{overall, round, pick_in_round, participant}
|
||||
"""
|
||||
if not self.draft_order:
|
||||
return []
|
||||
n = len(self.draft_order)
|
||||
count = count if count else len(self.draft_order)
|
||||
start = self.draft_index if from_overall is None else int(from_overall)
|
||||
start = start if include_current else start + 1
|
||||
|
||||
out: List[dict] = []
|
||||
for overall in range(start, start + count):
|
||||
r, p = _round_and_pick(overall, n)
|
||||
order_type = "snake"
|
||||
order = _round_order(r, order_type, self.draft_order)
|
||||
out.append({
|
||||
"overall": overall,
|
||||
"round": r,
|
||||
"pick_in_round": p,
|
||||
"participant": order[p - 1],
|
||||
})
|
||||
return out
|
||||
|
||||
# === Current Nomination / Bid ===
|
||||
def start_nomination(self, movie_id: int):
|
||||
@@ -155,6 +201,7 @@ class DraftStateManager:
|
||||
|
||||
# === Sync Snapshot ===
|
||||
def get_summary(self) -> dict:
|
||||
picks = self.next_picks(include_current=True)
|
||||
return {
|
||||
"phase": self.phase,
|
||||
"draft_order": self.draft_order,
|
||||
@@ -164,4 +211,18 @@ class DraftStateManager:
|
||||
# "bids": self.get_bids(),
|
||||
"bidding_timer_end": self.get_timer_end(),
|
||||
"bidding_timer_start": self.get_timer_start(),
|
||||
}
|
||||
"current_pick": picks[0] if picks else None,
|
||||
"next_picks": picks[1:] if picks else []
|
||||
}
|
||||
|
||||
OrderType = Literal["snake", "linear"]
|
||||
def _round_and_pick(overall: int, n: int) -> Tuple[int, int]:
|
||||
"""overall -> (round_1_based, pick_in_round_1_based)"""
|
||||
r = overall // n + 1
|
||||
p = overall % n + 1
|
||||
return r, p
|
||||
|
||||
def _round_order(round_num: int, order_type: OrderType, r1: Sequence[Any]) -> Sequence[Any]:
|
||||
if order_type == "linear" or (round_num % 2 == 1):
|
||||
return r1
|
||||
return list(reversed(r1)) # even rounds in snake
|
||||
@@ -1,14 +1,8 @@
|
||||
{% extends "base.dj.html" %}
|
||||
{% block content %}
|
||||
<h1>Draft Room: {{ league.name }} – {{ season.label }} {{ season.year }}</h1>
|
||||
{% block body %}
|
||||
{% load static %}
|
||||
<script>
|
||||
window.draftSessionId = "{{ draft_id_hashed }}"
|
||||
</script>
|
||||
<div id="draft-participant-root" data-draft-id="{{ draft_id_hashed }}"></div>
|
||||
{% if DEBUG %}
|
||||
<script src="http://localhost:3000/dist/bundle.js"></script>
|
||||
{% else %}
|
||||
<script src="{% static 'bundle.js' %}"></script>
|
||||
{% endif %}
|
||||
{% endblock content %}
|
||||
{% endblock body %}
|
||||
Reference in New Issue
Block a user