From 1504cf1cf5eba17a68000b2682bfca0ce58472fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D0=B4=D1=80=D0=B5=D0=B9=20=D0=A1=D1=83=D0=BC?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Sun, 2 Mar 2025 12:08:33 +0300 Subject: [PATCH] feat: added appointment of reviewer to a submission --- services/backend/api/v1/review/views.py | 17 +++++++++-- services/backend/api/v1/task/views.py | 1 + services/backend/apps/review/models.py | 3 +- .../0002_competitiontask_reviewers.py | 19 ++++++++++++ services/backend/apps/task/models.py | 29 +++++++++++++++++++ services/backend/apps/team/models.py | 1 - 6 files changed, 65 insertions(+), 5 deletions(-) create mode 100644 services/backend/apps/task/migrations/0002_competitiontask_reviewers.py diff --git a/services/backend/api/v1/review/views.py b/services/backend/api/v1/review/views.py index f8a83e8..4272739 100644 --- a/services/backend/api/v1/review/views.py +++ b/services/backend/api/v1/review/views.py @@ -41,7 +41,11 @@ def get_submission( review = Review.objects.get(reviewer=reviewer, submission=submission) if review.state == ReviewStatusChoices.NOT_CHECKED.value: review.state = ReviewStatusChoices.CHECKING.value + review.submission.state = ( + CompetitionTaskSubmission.StatusChoices.CHECKING.value + ) review.save() + review.submission.save() return status.OK, submission @@ -84,8 +88,17 @@ def evaluate_submission( points = 0 for criterea in evaluation: points += criterea["mark"] - review.submission.earned_points = points - + review.submission.earned_points = ( + points # TODO: оценка не от последнего проверяющего а средняя по всем + ) review.save() + all_checked = not submission.reviews.exclude( + state=ReviewStatusChoices.CHECKED + ).exists() + if all_checked: + review.submission.status = ( + CompetitionTaskSubmission.StatusChoices.CHECKED.value + ) + review.submission.save() return status.OK, review.submission diff --git a/services/backend/api/v1/task/views.py b/services/backend/api/v1/task/views.py index 79e9859..ebb0064 100644 --- a/services/backend/api/v1/task/views.py +++ b/services/backend/api/v1/task/views.py @@ -113,6 +113,7 @@ def submit_task( status=CompetitionTaskSubmission.StatusChoices.SENT, content=content, ) + submission.send_on_review() if task.type == CompetitionTask.CompetitionTaskType.CHECKER: submission = CompetitionTaskSubmission.objects.create( user=user, diff --git a/services/backend/apps/review/models.py b/services/backend/apps/review/models.py index 633c6fd..ba63376 100644 --- a/services/backend/apps/review/models.py +++ b/services/backend/apps/review/models.py @@ -1,7 +1,6 @@ from django.db import models from apps.core.models import BaseModel -from apps.task.models import CompetitionTaskSubmission class Reviewer(BaseModel): @@ -20,7 +19,7 @@ class ReviewStatusChoices(models.TextChoices): class Review(BaseModel): reviewer = models.ForeignKey(Reviewer, on_delete=models.CASCADE) submission = models.ForeignKey( - CompetitionTaskSubmission, + "task.CompetitionTaskSubmission", on_delete=models.CASCADE, related_name="reviews", ) diff --git a/services/backend/apps/task/migrations/0002_competitiontask_reviewers.py b/services/backend/apps/task/migrations/0002_competitiontask_reviewers.py new file mode 100644 index 0000000..fdf8acd --- /dev/null +++ b/services/backend/apps/task/migrations/0002_competitiontask_reviewers.py @@ -0,0 +1,19 @@ +# Generated by Django 5.1.6 on 2025-03-02 08:30 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('review', '0001_initial'), + ('task', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='competitiontask', + name='reviewers', + field=models.ManyToManyField(blank=True, to='review.reviewer'), + ), + ] diff --git a/services/backend/apps/task/models.py b/services/backend/apps/task/models.py index 66d4ca3..da528fb 100644 --- a/services/backend/apps/task/models.py +++ b/services/backend/apps/task/models.py @@ -1,10 +1,12 @@ from uuid import uuid4 from django.db import models +from django.db.models import Count, Q from tinymce.models import HTMLField from apps.competition.models import Competition from apps.core.models import BaseModel +from apps.review.models import Review, Reviewer, ReviewStatusChoices from apps.task.validators import ContestTaskCriteriesValidator from apps.user.models import User @@ -56,6 +58,9 @@ class CompetitionTask(BaseModel): verbose_name="критерии", ) + # only when "review" type + reviewers = models.ManyToManyField(Reviewer, blank=True) + def clean(self): ContestTaskCriteriesValidator()(self) @@ -116,3 +121,27 @@ class CompetitionTaskSubmission(BaseModel): reviewed_at = models.DateTimeField(null=True, blank=True) timestamp = models.DateTimeField(auto_now_add=True) + + def send_on_review(self): + if not self.task.reviewers.exists(): + return + + reviewer = ( + self.task.reviewers.annotate( + pending_count=Count( + "review", + filter=Q( + review__state__in=[ + ReviewStatusChoices.NOT_CHECKED, + ReviewStatusChoices.CHECKING, + ] + ), + ) + ) + .order_by("pending_count") + .first() + ) + review = Review.objects.create( + reviewer=reviewer, + submission=self, + ) diff --git a/services/backend/apps/team/models.py b/services/backend/apps/team/models.py index 8dd5b0c..b54dfc2 100644 --- a/services/backend/apps/team/models.py +++ b/services/backend/apps/team/models.py @@ -1,4 +1,3 @@ - from django.db import models from apps.core.models import BaseModel