Skip to content

Commit fd309d6

Browse files
committed
feat: add autoreloading on queue and viewing your own matches
[pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci
1 parent 5e2df55 commit fd309d6

5 files changed

Lines changed: 141 additions & 5 deletions

File tree

othello/apps/games/urls.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
path("watch/<int:game_id>", views.watch, name="watch"),
1313
path("replay/", views.replay, name="replay"),
1414
path("queue/", views.queue, name="queue"),
15+
path("queue/json/", views.queue_json, name="queue_json"),
1516
path("request_match/", views.request_match, name="request_match"),
1617
path("match_replay/<int:match_id>/", views.match_replay, name="match_replay"),
1718
path("help/", views.help_view, name="help"),

othello/apps/games/views.py

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from django.contrib.auth.decorators import login_required
66
from django.core.paginator import Paginator
77
from django.db import models
8-
from django.http import FileResponse, HttpRequest, HttpResponse
8+
from django.http import FileResponse, HttpRequest, HttpResponse, JsonResponse
99
from django.shortcuts import get_object_or_404, redirect, render
1010
from django.utils import timezone
1111

@@ -173,12 +173,59 @@ def request_match(request: HttpRequest) -> HttpResponse:
173173

174174

175175
@login_required
176+
def queue_json(request: HttpRequest) -> HttpResponse:
177+
matches = Match.objects.all().order_by("-created_at")
178+
my_matches_only = request.GET.get("my_matches") == "1"
179+
if my_matches_only:
180+
matches = matches.filter(
181+
models.Q(player1__user=request.user) | models.Q(player2__user=request.user)
182+
)
183+
paginator = Paginator(matches, 10)
184+
page_number = request.GET.get("page")
185+
page_obj = paginator.get_page(page_number)
186+
187+
matches_data = []
188+
for match in page_obj:
189+
matches_data.append(
190+
{
191+
"id": match.id,
192+
"player1_name": match.player1.get_game_name(),
193+
"player2_name": match.player2.get_game_name(),
194+
"score": f"{match.player1_wins}-{match.ties}-{match.player2_wins}"
195+
if match.status == "completed"
196+
else "-",
197+
"is_ranked": "Yes" if match.is_ranked else "No",
198+
"status": match.status,
199+
"created_at": match.created_at.isoformat(),
200+
"can_view_replay": request.user in [match.player1.user, match.player2.user],
201+
}
202+
)
203+
204+
return JsonResponse(
205+
{
206+
"matches": matches_data,
207+
"has_next": page_obj.has_next(),
208+
"has_previous": page_obj.has_previous(),
209+
"page_number": page_obj.number,
210+
}
211+
)
212+
213+
176214
def queue(request: HttpRequest) -> HttpResponse:
177215
matches = Match.objects.all().order_by("-created_at")
178-
paginator = Paginator(matches, 10) # 10 per page
216+
my_matches_only = request.GET.get("my_matches") == "1"
217+
if my_matches_only and request.user.is_authenticated:
218+
matches = matches.filter(
219+
models.Q(player1__user=request.user) | models.Q(player2__user=request.user)
220+
)
221+
paginator = Paginator(matches, 10)
179222
page_number = request.GET.get("page")
180223
page_obj = paginator.get_page(page_number)
181-
return render(request, "games/queue.html", {"page_obj": page_obj})
224+
return render(
225+
request,
226+
"games/queue.html",
227+
{"page_obj": page_obj, "my_matches_only": my_matches_only, "request": request},
228+
)
182229

183230

184231
def watch(request: HttpRequest, game_id: int | None = None) -> HttpResponse:

othello/settings/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@
144144

145145
# Celery
146146
CELERY_RESULT_BACKEND = "django-db"
147-
CELERY_BROKER_URL = "redis://localhost:6379/1"
147+
CELERY_BROKER_URL = "redis://othello_redis:6379/1"
148148
CELERY_TIMEZONE = "America/New_York"
149149
CELERY_BEAT_SCHEDULE = {
150150
"delete-old-games": {

othello/static/js/games/queue.js

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ $(document).ready(function() {
1515
};
1616

1717
function updateMatch(data) {
18+
console.log("websocket update");
1819
const matchRow = $(`.match[data-match-id="${data.match_id}"]`);
1920

2021
let statusHtml = data.status;
@@ -66,4 +67,81 @@ $(document).ready(function() {
6667
applyWinLoseClasses(newRow);
6768
}
6869
}
69-
});
70+
71+
$('#myMatchesCheckbox').change(function() {
72+
const url = new URL(window.location);
73+
if (this.checked) {
74+
url.searchParams.set('my_matches', '1');
75+
} else {
76+
url.searchParams.delete('my_matches');
77+
}
78+
window.location.href = url.toString();
79+
});
80+
81+
function pollQueue() {
82+
const url = new URL(window.location);
83+
84+
url.pathname = url.pathname.replace('/queue/', '/queue/json/');
85+
86+
if (!url.searchParams.has('page')) {
87+
url.searchParams.set('page', '1');
88+
}
89+
90+
$.getJSON(url.toString(), function(data) {
91+
updateTable(data.matches);
92+
});
93+
}
94+
95+
96+
function updateTable(matches) {
97+
console.log("http update");
98+
const tbody = $('#matches');
99+
tbody.empty();
100+
101+
matches.forEach(function(match) {
102+
let statusHtml = match.status;
103+
if (match.status === 'completed' && match.can_view_replay) {
104+
statusHtml += ` <a href="/match_replay/${match.id}/">(Replay)</a>`;
105+
}
106+
107+
const row = $(`
108+
<tr class="match" data-match-id="${match.id}">
109+
<td>${match.player1_name}</td>
110+
<td>${match.score}</td>
111+
<td>${match.player2_name}</td>
112+
<td>${match.is_ranked}</td>
113+
<td>${statusHtml}</td>
114+
<td>${match.created_at}</td>
115+
</tr>
116+
`);
117+
118+
row.find('td:nth-child(1), td:nth-child(3)')
119+
.removeClass('win lose');
120+
121+
if (match.status === 'completed' && match.score) {
122+
const parts = match.score.split('-').map(Number);
123+
124+
if (parts.length === 3) {
125+
const p1Wins = parts[0];
126+
const p2Wins = parts[2];
127+
128+
const p1Cell = row.find('td:nth-child(1)');
129+
const p2Cell = row.find('td:nth-child(3)');
130+
131+
if (p1Wins > p2Wins) {
132+
p1Cell.addClass('win');
133+
p2Cell.addClass('lose');
134+
} else if (p2Wins > p1Wins) {
135+
p2Cell.addClass('win');
136+
p1Cell.addClass('lose');
137+
}
138+
}
139+
}
140+
141+
tbody.append(row);
142+
});
143+
}
144+
145+
146+
setInterval(pollQueue, 10000);
147+
});

othello/templates/games/queue.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99

1010
{% block main %}
1111
<h1>Running Matches</h1>
12+
{% with query_params=request.GET.urlencode %}
13+
<div class="mb-3">
14+
<div class="form-check">
15+
<input class="form-check-input" type="checkbox" id="myMatchesCheckbox" {% if my_matches_only %}checked{% endif %}>
16+
<label class="form-check-label" for="myMatchesCheckbox">
17+
Show only my matches
18+
</label>
19+
</div>
20+
</div>
1221
<table class="table table-striped">
1322
<thead>
1423
<tr>
@@ -75,4 +84,5 @@ <h1>Running Matches</h1>
7584
{% endif %}
7685
</span>
7786
</div>
87+
{% endwith %}
7888
{% endblock %}

0 commit comments

Comments
 (0)