feat: added review logic

This commit is contained in:
Андрей Сумин
2025-03-01 17:20:45 +03:00
parent b7ae7e912e
commit 332ca694a8
9 changed files with 77 additions and 21 deletions
+6 -6
View File
@@ -4,8 +4,8 @@ from uuid import UUID
from django.http import HttpRequest from django.http import HttpRequest
from ninja import ModelSchema, Schema from ninja import ModelSchema, Schema
from apps.review.models import Reviewer from apps.review.models import Reviewer, Review
from apps.task.models import CompetetionTaskSumbission from apps.task.models import CompetitionTaskSubmission
class PingOut(Schema): class PingOut(Schema):
@@ -25,13 +25,13 @@ class SubmissionOut(ModelSchema):
status: Literal["sent", "checking", "checked"] status: Literal["sent", "checking", "checked"]
class Meta: class Meta:
model = CompetetionTaskSumbission model = CompetitionTaskSubmission
exclude = ("user",) exclude = ("user",)
class SubmissionsOut(Schema): class SubmissionsOut(Schema):
submissions: list[SubmissionOut] = [] submissions: list = None
@staticmethod @staticmethod
def resolve_submissions(self, context: HttpRequest) -> list[SubmissionOut]: def resolve_submissions(self, context) -> list[SubmissionOut]:
return list(CompetetionTaskSumbission.objects.all()) return list(Review.objects.filter(reviewer=context.get("request").auth))
+16 -1
View File
@@ -1,10 +1,15 @@
import logging
from http import HTTPStatus as status from http import HTTPStatus as status
from uuid import UUID
from django.http import HttpRequest from django.http import HttpRequest
from django.shortcuts import get_object_or_404
from ninja import Router from ninja import Router
from api.v1 import schemas as global_schemas from api.v1 import schemas as global_schemas
from api.v1.review import schemas from api.v1.review import schemas
from api.v1.task.schemas import TaskSubmissionIn
from apps.task.models import CompetitionTaskSubmission
router = Router(tags=["review"]) router = Router(tags=["review"])
@@ -16,7 +21,7 @@ router = Router(tags=["review"])
}, },
description="Список отправок, на проверку которых назначен ревьюер" description="Список отправок, на проверку которых назначен ревьюер"
) )
def get_submissions(request: HttpRequest, token) -> tuple[status, schemas.SubmissionsOut]: def get_submissions(request: HttpRequest, token: str) -> tuple[status, schemas.SubmissionsOut]:
return status.OK, schemas.SubmissionsOut() return status.OK, schemas.SubmissionsOut()
@@ -30,3 +35,13 @@ def get_submissions(request: HttpRequest, token) -> tuple[status, schemas.Submis
) )
def get_reviewer_profile(request: HttpRequest, token: str): def get_reviewer_profile(request: HttpRequest, token: str):
return status.OK, request.auth return status.OK, request.auth
@router.get(
"{token}/submissions/{submition_id}",
response={
status.OK: schemas.SubmissionOut,
},
)
def get_submission(request: HttpRequest, token: str, submition_id: UUID) -> tuple[status, schemas.SubmissionsOut]:
submission = get_object_or_404(CompetitionTaskSubmission, id=submition_id)
return status.OK, submission
+8 -8
View File
@@ -13,7 +13,7 @@ from api.v1.task.schemas import (
) )
from apps.competition.models import State from apps.competition.models import State
from apps.task.models import ( from apps.task.models import (
CompetetionTaskSumbission, CompetitionTaskSubmission,
Competition, Competition,
CompetitionTask, CompetitionTask,
) )
@@ -96,23 +96,23 @@ def submit_task(
) )
if task.type == CompetitionTask.CompetitionTaskType.INPUT: if task.type == CompetitionTask.CompetitionTaskType.INPUT:
CompetetionTaskSumbission.objects.create( CompetitionTaskSubmission.objects.create(
user=user, user=user,
task=task, task=task,
status=CompetetionTaskSumbission.StatusChoices.CHECKED, status=CompetitionTaskSubmission.StatusChoices.CHECKED,
result={"correct": submission.content == task.answer_file_path}, result={"correct": submission.content == task.answer_file_path},
) )
if task.type == CompetitionTask.CompetitionTaskType.REVIEW: if task.type == CompetitionTask.CompetitionTaskType.REVIEW:
CompetetionTaskSumbission.objects.create( CompetitionTaskSubmission.objects.create(
user=user, user=user,
task=task, task=task,
status=CompetetionTaskSumbission.StatusChoices.SENT, status=CompetitionTaskSubmission.StatusChoices.SENT,
) )
if task.type == CompetitionTask.CompetitionTaskType.CHECKER: if task.type == CompetitionTask.CompetitionTaskType.CHECKER:
CompetetionTaskSumbission.objects.create( CompetitionTaskSubmission.objects.create(
user=user, user=user,
task=task, task=task,
status=CompetetionTaskSumbission.StatusChoices.CHECKING, status=CompetitionTaskSubmission.StatusChoices.CHECKING,
) )
return TaskSubmissionOut(id=CompetetionTaskSumbission.id) return TaskSubmissionOut(id=CompetitionTaskSubmission.id)
@@ -41,6 +41,8 @@ class Competition(BaseModel):
def __str__(self): def __str__(self):
return self.title return self.title
class Meta: class Meta:
verbose_name = "соревнование" verbose_name = "соревнование"
verbose_name_plural = "соревнования" verbose_name_plural = "соревнования"
@@ -8,7 +8,7 @@ from django.core.management.base import BaseCommand
from django.utils import timezone from django.utils import timezone
from apps.competition.models import Competition, State from apps.competition.models import Competition, State
from apps.task.models import CompetetionTaskSumbission, CompetitionTask from apps.task.models import CompetitionTaskSubmission, CompetitionTask
from apps.user.models import User, UserRole from apps.user.models import User, UserRole
@@ -105,7 +105,7 @@ class Command(BaseCommand):
b"Submission content", b"Submission content",
name=f"submission_{uuid.uuid4().hex}.txt", name=f"submission_{uuid.uuid4().hex}.txt",
) )
submission = CompetetionTaskSumbission.objects.create( submission = CompetitionTaskSubmission.objects.create(
user=user, user=user,
task=task, task=task,
earned_points=random.randint( earned_points=random.randint(
+12
View File
@@ -1,6 +1,7 @@
from django.db import models from django.db import models
from apps.core.models import BaseModel from apps.core.models import BaseModel
from apps.task.models import CompetitionTaskSubmission
class Reviewer(BaseModel): class Reviewer(BaseModel):
@@ -8,3 +9,14 @@ class Reviewer(BaseModel):
surname = models.CharField(max_length=100) surname = models.CharField(max_length=100)
token = models.CharField(max_length=100) token = models.CharField(max_length=100)
class Review(BaseModel):
class ReviewStatusChoices(models.TextChoices):
NOT_CHECKED = "not_checked"
CHECKING = "checking"
CHECKED = "checked"
reviewer = models.ForeignKey(Reviewer, on_delete=models.CASCADE)
submission = models.ForeignKey(CompetitionTaskSubmission, on_delete=models.CASCADE)
state = models.CharField(choices=ReviewStatusChoices.choices, max_length=11)
@@ -38,8 +38,8 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)), ('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('status', models.CharField(choices=[('sent', 'Sent'), ('checking', 'Checking'), ('checked', 'Checked')], default='sent', max_length=8)), ('status', models.CharField(choices=[('sent', 'Sent'), ('checking', 'Checking'), ('checked', 'Checked')], default='sent', max_length=8)),
('content', models.FileField(upload_to=apps.task.models.CompetetionTaskSumbission.submission_content_upload_to)), ('content', models.FileField(upload_to=apps.task.models.CompetitionTaskSubmission.submission_content_upload_to)),
('stdout', models.FileField(blank=True, null=True, upload_to=apps.task.models.CompetetionTaskSumbission.submission_stdout_upload_to)), ('stdout', models.FileField(blank=True, null=True, upload_to=apps.task.models.CompetitionTaskSubmission.submission_stdout_upload_to)),
('result', models.JSONField(blank=True, default=None, null=True)), ('result', models.JSONField(blank=True, default=None, null=True)),
('earned_points', models.IntegerField()), ('earned_points', models.IntegerField()),
('timestamp', models.DateTimeField(auto_now_add=True)), ('timestamp', models.DateTimeField(auto_now_add=True)),
@@ -0,0 +1,19 @@
# Generated by Django 5.1.6 on 2025-03-01 12:54
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('review', '0001_initial'),
('task', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='competetiontasksumbission',
name='reviewers',
field=models.ManyToManyField(blank=True, related_name='reviewers', to='review.reviewer'),
),
]
+10 -2
View File
@@ -20,7 +20,7 @@ class CompetitionTask(BaseModel):
competition = models.ForeignKey(Competition, on_delete=models.CASCADE) competition = models.ForeignKey(Competition, on_delete=models.CASCADE)
title = models.CharField(verbose_name="заголовок", max_length=50) title = models.CharField(verbose_name="заголовок", max_length=50)
description = models.TextField(verbose_name="описание", max_length=300) description = models.TextField(verbose_name="описание", max_length=300)
max_attemps = models.PositiveSmallIntegerField(default=0) max_attemps = models.PositiveSmallIntegerField()
type = models.CharField( type = models.CharField(
choices=CompetitionTaskType, max_length=8, verbose_name="тип проверки" choices=CompetitionTaskType, max_length=8, verbose_name="тип проверки"
) )
@@ -50,6 +50,14 @@ class CompetitionTask(BaseModel):
blank=True, blank=True,
null=True, null=True,
verbose_name="критерии", verbose_name="критерии",
default=lambda: [
{
"name": "CHANGE ME",
"slug": "CHANGE ME",
"max_value": 0,
"min_value": 0,
}
],
) )
def clean(self): def clean(self):
@@ -63,7 +71,7 @@ class CompetitionTask(BaseModel):
verbose_name_plural = "задания" verbose_name_plural = "задания"
class CompetetionTaskSumbission(BaseModel): class CompetitionTaskSubmission(BaseModel):
class StatusChoices(models.TextChoices): class StatusChoices(models.TextChoices):
SENT = "sent" SENT = "sent"
CHECKING = "checking" CHECKING = "checking"