from datetime import timedelta from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.shortcuts import redirect, render from django.utils import timezone from apps.epic.models import GateSlot, Room, RoomInvite, debit_token, select_token RESERVE_TIMEOUT = timedelta(seconds=60) def _expire_reserved_slots(room): cutoff = timezone.now() - RESERVE_TIMEOUT room.gate_slots.filter( status=GateSlot.RESERVED, reserved_at__lt=cutoff, ).update(status=GateSlot.EMPTY, gamer=None, reserved_at=None) def _gate_context(room, user): _expire_reserved_slots(room) slots = room.gate_slots.order_by("slot_number") pending_slot = slots.filter(status=GateSlot.RESERVED).first() user_reserved_slot = None user_filled_slot = None if user.is_authenticated: user_reserved_slot = slots.filter(gamer=user, status=GateSlot.RESERVED).first() user_filled_slot = slots.filter(gamer=user, status=GateSlot.FILLED).first() eligible = ( user.is_authenticated and pending_slot is None and user_reserved_slot is None and user_filled_slot is None ) token_depleted = eligible and select_token(user) is None can_drop = eligible and not token_depleted is_last_slot = ( user_reserved_slot is not None and slots.filter(status=GateSlot.EMPTY).count() == 0 ) user_can_reject = user_reserved_slot is not None or user_filled_slot is not None return { "slots": slots, "pending_slot": pending_slot, "user_reserved_slot": user_reserved_slot, "user_filled_slot": user_filled_slot, "can_drop": can_drop, "token_depleted": token_depleted, "is_last_slot": is_last_slot, "user_can_reject": user_can_reject, } @login_required def create_room(request): if request.method == "POST": name = request.POST.get("name", "").strip() if name: room = Room.objects.create(name=name, owner=request.user) return redirect("epic:gatekeeper", room_id=room.id) return redirect("/gameboard/") def gatekeeper(request, room_id): room = Room.objects.get(id=room_id) ctx = _gate_context(room, request.user) ctx["room"] = room return render(request, "apps/gameboard/room.html", ctx) @login_required def drop_token(request, room_id): if request.method == "POST": room = Room.objects.get(id=room_id) if room.gate_slots.filter(status=GateSlot.RESERVED).exists(): return redirect("epic:gatekeeper", room_id=room_id) if room.gate_slots.filter(gamer=request.user, status=GateSlot.FILLED).exists(): return redirect("epic:gatekeeper", room_id=room_id) if select_token(request.user) is None: return HttpResponse(status=402) slot = room.gate_slots.filter( status=GateSlot.EMPTY ).order_by("slot_number").first() if slot: slot.gamer = request.user slot.status = GateSlot.RESERVED slot.reserved_at = timezone.now() slot.save() return redirect("epic:gatekeeper", room_id=room_id) @login_required def confirm_token(request, room_id): if request.method == "POST": room = Room.objects.get(id=room_id) slot = room.gate_slots.filter( gamer=request.user, status=GateSlot.RESERVED ).first() if slot: token = select_token(request.user) if token: debit_token(request.user, slot, token) return redirect("epic:gatekeeper", room_id=room_id) @login_required def reject_token(request, room_id): if request.method == "POST": room = Room.objects.get(id=room_id) slot = room.gate_slots.filter( gamer=request.user, status__in=[GateSlot.RESERVED, GateSlot.FILLED], ).first() if slot: slot.gamer = None slot.status = GateSlot.EMPTY slot.reserved_at = None slot.filled_at = None slot.save() return redirect("epic:gatekeeper", room_id=room_id) @login_required def invite_gamer(request, room_id): if request.method == "POST": room = Room.objects.get(id=room_id) email = request.POST.get("invitee_email", "").strip() if email: RoomInvite.objects.get_or_create( room=room, inviter=request.user, invitee_email=email, defaults={"status": RoomInvite.PENDING} ) return redirect("epic:gatekeeper", room_id=room_id) @login_required def delete_room(request, room_id): if request.method == "POST": room = Room.objects.get(id=room_id) if request.user == room.owner: room.delete() return redirect("/gameboard/") @login_required def abandon_room(request, room_id): if request.method == "POST": room = Room.objects.get(id=room_id) room.gate_slots.filter(gamer=request.user).update( gamer=None, status="EMPTY", filled_at=None ) room.invites.filter( invitee_email=request.user.email, status=RoomInvite.PENDING ).delete() return redirect("/gameboard/") def gate_status(request, room_id): room = Room.objects.get(id=room_id) if room.gate_status == Room.OPEN: return HttpResponse("") ctx = _gate_context(room, request.user) ctx["room"] = room return render(request, "apps/gameboard/_partials/_gatekeeper.html", ctx)