diff --git a/apps/tasks/tests.py b/apps/tasks/tests.py index 91c5cf4..820d732 100644 --- a/apps/tasks/tests.py +++ b/apps/tasks/tests.py @@ -326,9 +326,12 @@ def setUp(self) -> None: self.task = Task._default_manager.create( title="Example task", description="Some example task", + score=200, constraints=["A sad task constraint"], contest=self.contest, + output_file="Hello, World!\n", ) + self.user = User._default_manager.create( email="user@email.com", username="user", @@ -339,7 +342,7 @@ def setUp(self) -> None: author=self.user, task=self.task, code="print('Hello, World!')", - status=SubmissionStatus.ACCEPTED, + status=SubmissionStatus.WAITING_JUDGE, ) self.url = reverse("tasks:detail", args=[self.task.id]) @@ -452,6 +455,48 @@ def test_handle_submission_with_wrong_output(self) -> None: fetch_redirect_response=True, ) + def test_submission_has_accepted_status_increases_user_score(self) -> None: + handle_submission.apply( + args=(self.submission.code, self.task.id, self.submission.id) + ) + + self.submission.refresh_from_db() + self.user.refresh_from_db() + + self.assertEqual(self.submission.status, SubmissionStatus.ACCEPTED) + self.assertEqual(self.user.score, self.task.score) + + def test_submission_without_accepted_status_does_not_increase_user_score( + self, + ) -> None: + self.submission.code = 'print("Hello, Word")' + + handle_submission.apply( + args=(self.submission.code, self.task.id, self.submission.id) + ) + + self.user.refresh_from_db() + self.assertEqual(self.user.score, 0) + + def test_repeated_accepted_submission_cant_sum_to_user_score(self) -> None: + handle_submission.apply( + args=(self.submission.code, self.task.id, self.submission.id) + ) + + second_submission = Submission._default_manager.create( + author=self.user, + task=self.task, + code="print('Hello, World!')", + status=SubmissionStatus.WAITING_JUDGE, + ) + + handle_submission.apply( + args=(second_submission.code, self.task.id, second_submission.id) + ) + + self.user.refresh_from_db() + self.assertEqual(self.user.score, self.task.score) + def test_form_success_url(self) -> None: url = reverse("submissions:list") self.assertEqual(self.view.get_success_url(), url) diff --git a/apps/tasks/views.py b/apps/tasks/views.py index fae6aa5..0238fd4 100644 --- a/apps/tasks/views.py +++ b/apps/tasks/views.py @@ -9,7 +9,7 @@ from django.views.generic.edit import FormMixin from apps.submissions.forms import SubmissionForm -from apps.submissions.models import Submission +from apps.submissions.models import Submission, SubmissionStatus from apps.tasks.models import Task from server import celery @@ -49,6 +49,22 @@ def handle_submission(code: str, task_id: int, submission_id: int) -> None: submission.status = "AC" if output == task.output_file else "WA" submission.save() + has_already_scored = ( + Submission._default_manager.filter( + author=submission.author, + task=task, + status=SubmissionStatus.ACCEPTED, + ) + .exclude(id=submission.id) + .exists() + ) + + is_accepted = submission.status == SubmissionStatus.ACCEPTED + + if is_accepted and not has_already_scored: + submission.author.score += task.score + submission.author.save() + class DetailView(FormMixinBase, DetailViewBase): model = Task diff --git a/apps/users/admin.py b/apps/users/admin.py index 3a345a6..b6928d8 100644 --- a/apps/users/admin.py +++ b/apps/users/admin.py @@ -7,9 +7,12 @@ @register(User) class UserAdmin(DefaultUserAdmin): - list_display = ("username", "email", "is_staff", "is_active") + list_display = ("username", "email", "is_staff", "is_active", "score") fieldsets = [ - (_("Personal info"), {"fields": ("username", "email", "password")}), + ( + _("Personal info"), + {"fields": ("username", "email", "password", "score")}, + ), ( _("Permissions"), { @@ -28,7 +31,13 @@ class UserAdmin(DefaultUserAdmin): None, { "classes": ("wide",), - "fields": ("username", "email", "password1", "password2"), + "fields": ( + "username", + "email", + "password1", + "password2", + "score", + ), }, ), ) diff --git a/apps/users/migrations/0002_user_score.py b/apps/users/migrations/0002_user_score.py new file mode 100644 index 0000000..a937658 --- /dev/null +++ b/apps/users/migrations/0002_user_score.py @@ -0,0 +1,17 @@ +# Generated by Django 4.2.7 on 2023-12-01 19:35 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("users", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="user", + name="score", + field=models.IntegerField(default=0), + ), + ] diff --git a/apps/users/models.py b/apps/users/models.py index 542ad76..901f04b 100644 --- a/apps/users/models.py +++ b/apps/users/models.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING, Any, List from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin -from django.db.models import BooleanField, CharField, EmailField +from django.db.models import BooleanField, CharField, EmailField, IntegerField from apps.users.managers import UserManager from core.models import TimestampedModel @@ -19,6 +19,7 @@ class User(AbstractBaseUser, PermissionsMixin, TimestampedModel): email = EmailField(db_index=True, max_length=256, unique=True) username = CharField(db_index=True, max_length=128, unique=True) + score = IntegerField(default=0) # When a user no longer wishes to use our platform, they may try to # delete there account. That's a problem for us because the data we diff --git a/apps/users/tests.py b/apps/users/tests.py index 1d5930d..db4fd55 100644 --- a/apps/users/tests.py +++ b/apps/users/tests.py @@ -44,7 +44,13 @@ def test_create_superuser(self) -> None: class UserAdminTestCase(TestCase): def test_list_display(self) -> None: list_display = UserAdmin.list_display - expected = ("username", "email", "is_staff", "is_active") + expected = ( + "username", + "email", + "is_staff", + "is_active", + "score", + ) self.assertEqual(list_display, expected) @@ -58,7 +64,7 @@ def test_fieldsets(self) -> None: expected = [ ( _("Personal info"), - {"fields": ("username", "email", "password")}, + {"fields": ("username", "email", "password", "score")}, ), ( _("Permissions"), @@ -82,7 +88,13 @@ def test_add_fieldsets(self) -> None: None, { "classes": ("wide",), - "fields": ("username", "email", "password1", "password2"), + "fields": ( + "username", + "email", + "password1", + "password2", + "score", + ), }, ), ) diff --git a/templates/users/profile.html b/templates/users/profile.html index 461dec9..cdbf56c 100644 --- a/templates/users/profile.html +++ b/templates/users/profile.html @@ -19,7 +19,7 @@

{{ user.username }}

{% if request.user.is_authenticated and request.user == user %}

E-mail: {{ user.email }}

{% endif %} -

Score: 0

+

Score: {{ user.score }}

Contests: {{ user.contests|length }}