Skip to content
This repository has been archived by the owner on Jan 3, 2024. It is now read-only.

[feature request]: Users ranking page #111

Merged
merged 5 commits into from
Dec 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions apps/users/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,32 @@ def test_email(self) -> None:

self.assertEqual(response.status_code, 200)
self.assertContains(response, user.email)


class RankingListViewtest(TestCase):
def setUp(self) -> None:
self.user = User._default_manager.create(
username="testuser1",
email="testuser@example1",
password="testpassword1",
score=100,
)
self.user = User._default_manager.create(
username="testuser2",
email="testuser@example2",
password="testpassword2",
score=90,
)

def test_ranking_list_view(self) -> None:
self.client.login(email="testuser@example", password="testpassword")

url = reverse("users:ranking")
response = self.client.get(url)
ranking_order = [user.username for user in response.context["ranking"]]
expected_order = ["testuser1", "testuser2"]

self.assertEqual(response.status_code, 200)
self.assertIn("ranking", response.context)
self.assertIn(self.user, response.context["ranking"])
self.assertEqual(ranking_order, expected_order)
3 changes: 2 additions & 1 deletion apps/users/urls.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from django.urls import path

from apps.users.views import ProfileView, RegisterView
from apps.users.views import ProfileView, RankingView, RegisterView

app_name = "users"

urlpatterns = [
path("register/", RegisterView.as_view(), name="register"),
path("profile/<str:username>/", ProfileView.as_view(), name="profile"),
path("ranking/", RankingView.as_view(), name="ranking"),
]
19 changes: 19 additions & 0 deletions apps/users/views.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
from typing import TYPE_CHECKING

from django.contrib.auth import login
from django.db.models import QuerySet
from django.http import HttpRequest, HttpResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.views import View
from django.views.generic import ListView

from apps.users.forms import CreateUserForm
from apps.users.models import User

if TYPE_CHECKING:
RankingViewBase = ListView[User]
else:
RankingViewBase = ListView


class RegisterView(View):
template_name = "registration/register.html"
Expand Down Expand Up @@ -34,3 +43,13 @@ class ProfileView(View):
def get(self, request: HttpRequest, *, username: str) -> HttpResponse:
user = get_object_or_404(User, username=username)
return render(request, self.template_name, {"user": user})


class RankingView(RankingViewBase):
model = User
template_name = "ranking/list.html"
context_object_name = "ranking"
paginate_by = 10

def get_queryset(self) -> QuerySet[User]:
return User._default_manager.all().order_by("-score")
2 changes: 1 addition & 1 deletion templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@
<li class="nav-item">
</li>
<li class="nav-item">
<a class="nav-link text-white" href="#">
<a class="nav-link text-white" href="{% url 'users:ranking' %}">
Ranking
</a>
</li>
Expand Down
96 changes: 96 additions & 0 deletions templates/ranking/list.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
{% extends "base.html" %}

{% load crispy_forms_tags %}

{% block title %}Ranking{% endblock title %}

{% block head %}
<style>
.center-table {
display: flex;
justify-content: center;
}
table {
border-collapse: collapse;
width: 100%;
margin: 20px 0;
}
th, td {
border: 1px solid #dddddd;
text-align: left;
padding: 8px;
text-align: center;
}
th {
background-color: #f2f2f2;
}
</style>
{% endblock head %}

{% block content %}
<h2>Ranking</h2>
<table class="table table-striped">
<thead>
<tr>
<th>Rank</th>
<th>User</th>
<th>Score</th>
</tr>
</thead>
<tbody>
{% for user in page_obj %}
<tr>
<td>{{ forloop.counter }}</td>
<td>
<a href="{% url 'users:profile' user.username %}">
{{ user.username }}
</a>
</td>
<td>{{ user.score }}</td>
</tr>
{% endfor %}
</tbody>
</table>

<nav aria-label="Page navigation example">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1" tabindex="-1">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" tabindex="-1" aria-disabled="true">Previous</a>
</li>
{% endif %}

{% for i in page_obj.paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active">
<a class="page-link" href="?page={{ i }}">{{ i }} </a>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ i }}">{{ i }}</a>
</li>
{% endif %}
{% endfor %}

{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
</li>
{% else %}
<li class="page-item disabled">
<a class="page-link" href="#" aria-disabled="true">Next</a>
</li>
{% endif %}
</ul>
</nav>
{% endblock content %}