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 documentation/fix-and-finalize-docs
Browse files Browse the repository at this point in the history
  • Loading branch information
thegm445 committed Dec 13, 2023
2 parents eac81a5 + 1fb5285 commit 6fa47bd
Show file tree
Hide file tree
Showing 17 changed files with 389 additions and 121 deletions.
10 changes: 5 additions & 5 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
Expand All @@ -15,25 +15,25 @@ repos:
- id: detect-private-key
- id: end-of-file-fixer
- repo: https://github.com/commitizen-tools/commitizen
rev: 3.9.0
rev: v3.13.0
hooks:
- id: commitizen
- id: commitizen-branch
stages: [push]
- repo: https://github.com/psf/black
rev: 23.9.1
rev: 23.12.0
hooks:
- id: black
- repo: https://github.com/pycqa/isort
rev: 5.12.0
rev: 5.13.1
hooks:
- id: isort
- repo: https://github.com/pycqa/flake8
rev: 6.1.0
hooks:
- id: flake8
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.6.1
rev: v1.7.1
hooks:
- id: mypy
additional_dependencies:
Expand Down
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
46 changes: 36 additions & 10 deletions apps/tasks/views.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import sys
from io import StringIO
from typing import TYPE_CHECKING, Any, Dict
from resource import RLIMIT_AS, getrlimit, setrlimit
from signal import SIGALRM, alarm, signal
from typing import TYPE_CHECKING, Any, Dict, Optional

from django.http import HttpRequest, HttpResponse
from django.shortcuts import redirect
Expand All @@ -21,31 +23,56 @@
FormMixinBase = FormMixin


def signal_handler(signum: int, frame: object) -> None:
raise TimeoutError("Time limit exceeded")


@celery.task(ignore_result=True)
def handle_submission(code: str, task_id: int, submission_id: int) -> None:
task = Task._default_manager.get(id=task_id)
submission = Submission._default_manager.get(id=submission_id)

if task.memory_limit is not None:
_, hard = getrlimit(RLIMIT_AS)
setrlimit(RLIMIT_AS, (task.memory_limit, hard))

if task.time_limit is not None:
signal(SIGALRM, signal_handler)
alarm(task.time_limit)

if (output := compile_code(submission, task, code)) is None:
return

check_answer(submission, task, output)


def compile_code(
submission: Submission, task: Task, code: str
) -> Optional[str]:
input_data = StringIO(task.input_file)

old_stdin = sys.stdin
sys.stdin = input_data

old_stdout = sys.stdout
sys.stdout = stdout = StringIO()

try:
eval(compile(code, "<string>", "exec"))
except MemoryError: # pragma: no cover
submission.status = "MLE"
submission.save()
return None
except TimeoutError: # pragma: no cover
submission.status = "TLE"
submission.save()
return None
except Exception:
submission.status = "RE"
submission.save()
return
finally:
sys.stdout = old_stdout
sys.stdin = old_stdin
return None

return stdout.getvalue()

output = stdout.getvalue()

def check_answer(submission: Submission, task: Task, output: str) -> None:
submission.status = "AC" if output == task.output_file else "WA"
submission.save()

Expand All @@ -58,7 +85,6 @@ def handle_submission(code: str, task_id: int, submission_id: int) -> None:
.exclude(id=submission.id)
.exists()
)

is_accepted = submission.status == SubmissionStatus.ACCEPTED

if is_accepted and not has_already_scored:
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
1 change: 1 addition & 0 deletions out.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Hello, world!
Loading

0 comments on commit 6fa47bd

Please sign in to comment.