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

Commit

Permalink
Merge branch 'main' into feature/limits
Browse files Browse the repository at this point in the history
  • Loading branch information
bitterteriyaki committed Dec 12, 2023
2 parents 1edec7e + e43d90c commit 8107ed0
Show file tree
Hide file tree
Showing 13 changed files with 346 additions and 106 deletions.
11 changes: 6 additions & 5 deletions apps/submissions/forms.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
from django.forms import CharField, Form, Textarea
from django.forms import Form
from djangocodemirror.fields import CodeMirrorField


class SubmissionForm(Form):
code = CharField(
label="Source Code",
required=True,
code = CodeMirrorField(
min_length=15,
widget=Textarea(attrs={"rows": 12, "style": "width: 100%;"}),
label="Code",
required=True,
config_name="python",
)
9 changes: 9 additions & 0 deletions apps/tasks/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,15 @@ def test_form_success_url(self) -> None:
url = reverse("submissions:list")
self.assertEqual(self.view.get_success_url(), url)

def test_invalid_form_submission(self) -> None:
self.client.force_login(self.user)

response = self.client.post(self.url, data={"code": ""})

self.assertEqual(response.status_code, 200)
self.assertTemplateUsed(response, "tasks/detail.html")
self.assertFalse(response.context["form"].is_valid())


class BackgroundJobTaskTest(TestCase):
def setUp(self) -> None:
Expand Down
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: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ services:
- config/.env
depends_on:
- postgres
- rabbitmq
- celery
command: python -Wd manage.py runserver 0.0.0.0:8000

celery:
Expand Down
185 changes: 101 additions & 84 deletions poetry.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ crispy-bootstrap5 = "^2023.10"
gunicorn = "^21.2.0"
whitenoise = "^6.6.0"
celery = {extras = ["librabbitmq"], version = "^5.3.6"}
djangocodemirror = "^2.1.0"

[tool.poetry.group.dev.dependencies]
pre-commit = "^3.5.0"
Expand Down
3 changes: 3 additions & 0 deletions server/settings/base.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
from os.path import join
from typing import List

from djangocodemirror.settings import *

from server.settings import BASE_DIR, env

######################
Expand Down Expand Up @@ -78,6 +80,7 @@
"bootstrap5",
"crispy_forms",
"crispy_bootstrap5",
"djangocodemirror",
]

LOCAL_APPS = [
Expand Down
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 %}
34 changes: 29 additions & 5 deletions templates/submissions/list.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,24 +6,39 @@

{% 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;
}

.center-table {
display: flex;
justify-content: center;
}

.success {
background-color: #47fc00b7;
}

.waiting {
background-color: #ff88008c;
}

.wrong {
background-color: #ff00006b;
}
</style>
{% endblock head %}

Expand All @@ -41,7 +56,16 @@ <h2>Submissions</h2>
</thead>
<tbody>
{% for submission in page_obj %}
<tr>
<tr
class="
{% if submission.status == 'AC' %}
success
{% elif submission.status == 'WJ' %}
waiting
{% else %}
wrong
{% endif %}
">
<td>{{ submission.id }}</td>
<td>
<a href="{% url 'users:profile' submission.author.username %}">
Expand Down
58 changes: 48 additions & 10 deletions templates/tasks/detail.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,27 @@
{% extends "base.html" %}

{% load crispy_forms_tags %}
{% load djangocodemirror_tags %}

{% block title %}{{ task.title }}{% endblock title %}

{% block content %}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/theme/dracula.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/theme/monokai.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/theme/3024-day.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/theme/3024-night.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/theme/cobalt.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/theme/eclipse.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/theme/rubyblue.min.css">

<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/mode/python/python.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/addon/edit/closebrackets.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/addon/edit/matchbrackets.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/addon/edit/matchtags.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/addon/edit/closetag.min.js"></script>

<h1>
<a href="{% url 'tasks:detail' task.id %}">
{{ task.title }}
Expand Down Expand Up @@ -54,17 +71,38 @@ <h4>Constraints</h4>
<form method="post">
{% csrf_token %}

<div class="row justify-content-center">
<br>
<h4>Submit</h4>
{{ form|crispy }}
<input
type="submit"
class="btn btn-primary"
style="width: 40%; margin-top: 25px;"
value="Submit"
>
<h4>Submit</h4>

<div style="display: flex; flex-direction: column; gap: 20px; justify-content: center;">
<div style="display: flex; flex-direction: column; gap: 25px; font-size: 0.8rem;">
<textarea
id="id_code"
name="code"
class="form-control"
></textarea>
</div>

<input
type="submit"
class="btn btn-primary"
style="width: 100px;"
value="Submit"
>
</div>
</div>
</form>

<script>
var codemirror = CodeMirror.fromTextArea(
document.getElementById("id_code"),
{
mode: "python",
theme: "dracula",
lineNumbers: true,
autoCloseBrackets: true,
matchBrackets: true,
}
);
</script>

{% endblock content %}

0 comments on commit 8107ed0

Please sign in to comment.