-
Notifications
You must be signed in to change notification settings - Fork 6
add unified queue for match running #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 14 commits
0079636
053a428
031d305
857b5b4
3b46d19
e758094
5f748af
190c413
5e2df55
fd309d6
67143fd
c4c543a
54b43ef
766ea9c
916b70f
210515b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please squash all the migrations into one big migration file |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # Generated by Django 5.2.10 on 2026-02-02 20:22 | ||
|
|
||
| import django.db.models.deletion | ||
| from django.conf import settings | ||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('authentication', '0009_alter_user_options_alter_user_id'), | ||
| ('games', '0033_match_is_tie_match_loser_match_player1_wins_and_more'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name='user', | ||
| name='rating', | ||
| field=models.DecimalField(decimal_places=2, default=0.0, max_digits=6), | ||
| ), | ||
| migrations.CreateModel( | ||
| name='RatingHistory', | ||
| fields=[ | ||
| ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | ||
| ('rating', models.DecimalField(decimal_places=2, max_digits=6)), | ||
| ('changed_at', models.DateTimeField(auto_now_add=True)), | ||
| ('match', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='games.match')), | ||
| ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rating_history', to=settings.AUTH_USER_MODEL)), | ||
| ], | ||
| options={ | ||
| 'ordering': ['changed_at'], | ||
| }, | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| # Generated by Django 5.2.10 on 2026-02-02 23:15 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('authentication', '0010_user_rating_ratinghistory'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AlterField( | ||
| model_name='user', | ||
| name='rating', | ||
| field=models.DecimalField(decimal_places=2, default=1200.0, max_digits=6), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| # Generated by Django 5.2.10 on 2026-02-02 23:27 | ||
|
|
||
| from django.db import migrations, models | ||
|
|
||
|
|
||
| class Migration(migrations.Migration): | ||
|
|
||
| dependencies = [ | ||
| ('authentication', '0011_alter_user_rating'), | ||
| ] | ||
|
|
||
| operations = [ | ||
| migrations.AddField( | ||
| model_name='user', | ||
| name='accept_ranked_matches', | ||
| field=models.BooleanField(default=True, help_text='Allow any user to request ranked matches against you'), | ||
| ), | ||
| migrations.AddField( | ||
| model_name='user', | ||
| name='accept_unranked_matches', | ||
| field=models.BooleanField(default=True, help_text='Allow any user to request unranked matches against you'), | ||
| ), | ||
| ] |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,8 +1,36 @@ | ||||||||||||||||||||||
| import json | ||||||||||||||||||||||
| import logging | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| from django.contrib import messages | ||||||||||||||||||||||
| from django.contrib.auth import get_user_model | ||||||||||||||||||||||
| from django.contrib.auth.decorators import login_required | ||||||||||||||||||||||
| from django.core.paginator import Paginator | ||||||||||||||||||||||
| from django.http import HttpRequest, HttpResponse | ||||||||||||||||||||||
| from django.shortcuts import render | ||||||||||||||||||||||
| from django.shortcuts import get_object_or_404, redirect, render | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| from ..games.forms import MatchForm | ||||||||||||||||||||||
| from ..games.models import RatingHistory, Submission | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| logger = logging.getLogger("othello") | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| def index(request: HttpRequest) -> HttpResponse: | ||||||||||||||||||||||
| if request.user.is_authenticated: | ||||||||||||||||||||||
|
amcsz marked this conversation as resolved.
|
||||||||||||||||||||||
| user = request.user | ||||||||||||||||||||||
| history = RatingHistory.objects.filter(user=user).order_by("changed_at") | ||||||||||||||||||||||
|
amcsz marked this conversation as resolved.
|
||||||||||||||||||||||
| ratings = [float(i) for i in history.values_list("rating", flat=True)] | ||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Shouldn't this already be a "list" of floats? |
||||||||||||||||||||||
| dates = [h.changed_at.strftime("%Y-%m-%d %H:%M") for h in history] | ||||||||||||||||||||||
| return render( | ||||||||||||||||||||||
| request, | ||||||||||||||||||||||
| "auth/index.html", | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| "current_rating": user.rating, | ||||||||||||||||||||||
| "ratings_json": json.dumps(ratings), | ||||||||||||||||||||||
| "dates_json": json.dumps(dates), | ||||||||||||||||||||||
| "accept_ranked_matches": user.accept_ranked_matches, | ||||||||||||||||||||||
| "accept_unranked_matches": user.accept_unranked_matches, | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
| return render(request, "auth/index.html") | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
@@ -12,3 +40,74 @@ def login(request: HttpRequest) -> HttpResponse: | |||||||||||||||||||||
|
|
||||||||||||||||||||||
| def error(request: HttpRequest) -> HttpResponse: | ||||||||||||||||||||||
| return render(request, "auth/error.html") | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @login_required | ||||||||||||||||||||||
| def rating(request: HttpRequest) -> HttpResponse: | ||||||||||||||||||||||
| user = request.user | ||||||||||||||||||||||
| history = RatingHistory.objects.filter(user=user).order_by("changed_at") | ||||||||||||||||||||||
| ratings = list(history.values_list("rating", flat=True)) | ||||||||||||||||||||||
| dates = list(history.values_list("changed_at", flat=True)) | ||||||||||||||||||||||
| return render( | ||||||||||||||||||||||
| request, | ||||||||||||||||||||||
| "auth/rating.html", | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| "current_rating": user.rating, | ||||||||||||||||||||||
| "ratings": ratings, | ||||||||||||||||||||||
| "dates": [d.isoformat() for d in dates], | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @login_required | ||||||||||||||||||||||
| def profile(request: HttpRequest, username: str) -> HttpResponse: | ||||||||||||||||||||||
| profile_user = get_object_or_404(get_user_model(), username=username) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| history = RatingHistory.objects.filter(user=profile_user).order_by("changed_at") | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| ratings = [float(i) for i in history.values_list("rating", flat=True)] | ||||||||||||||||||||||
| dates = [h.changed_at.strftime("%Y-%m-%d %H:%M") for h in history] | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| return render( | ||||||||||||||||||||||
| request, | ||||||||||||||||||||||
| "auth/profile.html", | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| "profile_user": profile_user, | ||||||||||||||||||||||
| "current_rating": profile_user.rating, | ||||||||||||||||||||||
| "ratings_json": json.dumps(ratings), | ||||||||||||||||||||||
| "dates_json": json.dumps(dates), | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @login_required | ||||||||||||||||||||||
| def rankings(request: HttpRequest) -> HttpResponse: | ||||||||||||||||||||||
| users = get_user_model().objects.order_by("-rating").exclude(username="Yourself") | ||||||||||||||||||||||
| paginator = Paginator(users, 25) # Show 25 users per page | ||||||||||||||||||||||
| page_number = request.GET.get("page") | ||||||||||||||||||||||
| page_obj = paginator.get_page(page_number) | ||||||||||||||||||||||
| form = MatchForm(request.user) | ||||||||||||||||||||||
| users_with_submissions = set( | ||||||||||||||||||||||
| Submission.objects.values_list("user__username", flat=True).distinct() | ||||||||||||||||||||||
| ) - {request.user.username, "Yourself"} | ||||||||||||||||||||||
|
Comment on lines
+90
to
+92
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wouldn't it be clearer to do something like
Suggested change
|
||||||||||||||||||||||
| return render( | ||||||||||||||||||||||
| request, | ||||||||||||||||||||||
| "auth/rankings.html", | ||||||||||||||||||||||
| { | ||||||||||||||||||||||
| "page_obj": page_obj, | ||||||||||||||||||||||
| "form": form, | ||||||||||||||||||||||
| "users_with_submissions": users_with_submissions, | ||||||||||||||||||||||
| }, | ||||||||||||||||||||||
| ) | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
|
|
||||||||||||||||||||||
| @login_required | ||||||||||||||||||||||
| def update_preferences(request: HttpRequest) -> HttpResponse: | ||||||||||||||||||||||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You probably want |
||||||||||||||||||||||
| if request.method == "POST": | ||||||||||||||||||||||
| accept_ranked = request.POST.get("accept_ranked_matches") == "true" | ||||||||||||||||||||||
| accept_unranked = request.POST.get("accept_unranked_matches") == "true" | ||||||||||||||||||||||
| request.user.accept_ranked_matches = accept_ranked | ||||||||||||||||||||||
| request.user.accept_unranked_matches = accept_unranked | ||||||||||||||||||||||
|
Comment on lines
+107
to
+110
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why isn't this in a |
||||||||||||||||||||||
| request.user.save(update_fields=["accept_ranked_matches", "accept_unranked_matches"]) | ||||||||||||||||||||||
| messages.success(request, "Preferences updated successfully.") | ||||||||||||||||||||||
| return redirect("auth:index") | ||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,6 @@ | ||
| from django.contrib import admin | ||
|
|
||
| from .models import Game, GameError, Submission | ||
| from .models import Game, GameError, Match, RatingHistory, Submission | ||
|
|
||
|
|
||
| class GameErrorAdmin(admin.TabularInline): | ||
|
|
@@ -18,5 +18,15 @@ class SubmissionAdmin(admin.ModelAdmin): | |
| readonly_fields = ("id",) | ||
|
|
||
|
|
||
| class MatchAdmin(admin.ModelAdmin): | ||
| readonly_fields = ("id",) | ||
|
|
||
|
|
||
| class RatingHistoryAdmin(admin.ModelAdmin): | ||
| readonly_fields = ("id",) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we customize these admins a bit more? At a minimum, it would be nice to have |
||
|
|
||
|
|
||
| admin.site.register(Game, GameAdmin) | ||
| admin.site.register(Submission, SubmissionAdmin) | ||
| admin.site.register(Match, MatchAdmin) | ||
| admin.site.register(RatingHistory, RatingHistoryAdmin) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're going to do this, you should probably
admin.set_password("tjcsl"), document it, and add a password login whensettings.DEBUG is TrueThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the
admin.set_password("tjcsl")part, wouldn't it be just the same as calling create_superuser? Also, why would you need to only add it on settings.DEBUG = True when it's always going to be true since its a testing environment?