Merge branch 'master' of gitlab.prodcontest.ru:team-15/project

This commit is contained in:
ITQ
2025-03-02 12:35:20 +03:00
14 changed files with 6112 additions and 59 deletions
+16 -3
View File
@@ -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
@@ -79,13 +83,22 @@ def evaluate_submission(
evaluation = evaluation_info.dict()["evaluation"]
review.evaluation = evaluation
review.state = ReviewStatusChoices.CHECKED.value
review.submission.reviewed_at = datetime.now()
review.submission.checked_at = datetime.now()
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
+8 -2
View File
@@ -3,7 +3,7 @@ from uuid import UUID
from ninja import ModelSchema, Schema
from apps.task.models import CompetitionTask, CompetitionTaskSubmission
from apps.task.models import CompetitionTask, CompetitionTaskSubmission, CompetitionTaskAttachment
class TaskOutSchema(ModelSchema):
@@ -29,4 +29,10 @@ class HistorySubmissionOut(ModelSchema):
class Meta:
model = CompetitionTaskSubmission
fields = ("id", "earned_points", "timestamp")
fields = ("id", "earned_points", "timestamp", "content",)
class TaskAttachmentSchema(ModelSchema):
class Meta:
model = CompetitionTaskAttachment
fields = ("id", "file", "public",)
+17
View File
@@ -8,6 +8,7 @@ from api.v1.ping.schemas import PingOut
from api.v1.schemas import ForbiddenError, NotFoundError, UnauthorizedError
from api.v1.task.schemas import (
HistorySubmissionOut,
TaskAttachmentSchema,
TaskOutSchema,
TaskSubmissionOut,
)
@@ -15,6 +16,7 @@ from apps.competition.models import State
from apps.task.models import (
Competition,
CompetitionTask,
CompetitionTaskAttachment,
CompetitionTaskSubmission,
)
@@ -113,6 +115,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,
@@ -140,3 +143,17 @@ def get_submissions_history(request, competition_id: UUID, task_id: UUID):
)
return status.OK, submissions_history
@router.get(
"competitions/{competition_id}/tasks/{task_id}/attachments",
response={
status.OK: list[TaskAttachmentSchema],
status.UNAUTHORIZED: UnauthorizedError,
},
)
def get_task_attachments(request, competition_id: UUID, task_id: UUID):
task = get_object_or_404(CompetitionTask, id=task_id)
return status.OK, CompetitionTaskAttachment.objects.filter(
competition_id=competition_id, task=task, user=request.auth
)
+17
View File
@@ -0,0 +1,17 @@
from django.contrib import admin
from apps.review.models import Review, Reviewer
@admin.register(Reviewer)
class ReviewAdmin(admin.ModelAdmin):
list_display = ("name", "surname",)
search_fields = ("name", "surname",)
@admin.register(Review)
class ReviewAdmin(admin.ModelAdmin):
list_display = ("id", "reviewer", "submission",)
search_fields = ("id", "reviewer__id", "reviewer__name", "reviewer__surname",
"submission__id", "submission__content")
list_filter = ("submission__plagiarism_checked", "submission__status",)
+1
View File
@@ -4,3 +4,4 @@ from django.apps import AppConfig
class CoreConfig(AppConfig):
name = "apps.review"
label = "review"
verbose_name = "Проверка"
@@ -1,5 +1,6 @@
# Generated by Django 5.1.6 on 2025-03-02 06:13
# Generated by Django 5.1.6 on 2025-03-02 09:31
import django.db.models.deletion
import uuid
from django.db import migrations, models
@@ -9,30 +10,35 @@ class Migration(migrations.Migration):
initial = True
dependencies = [
('task', '0003_remove_competitiontask_attachments'),
]
operations = [
migrations.CreateModel(
name='Review',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('evaluation', models.JSONField(blank=True, default=list, null=True)),
('state', models.CharField(choices=[('not_checked', 'Not Checked'), ('checking', 'Checking'), ('checked', 'Checked')], default='not_checked', max_length=11)),
],
options={
'abstract': False,
},
),
migrations.CreateModel(
name='Reviewer',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('name', models.CharField(max_length=100)),
('surname', models.CharField(max_length=100)),
('token', models.CharField(max_length=100)),
('name', models.CharField(max_length=100, verbose_name='имя')),
('surname', models.CharField(max_length=100, verbose_name='фамилия')),
('token', models.CharField(max_length=100, verbose_name='токен для входа')),
],
options={
'abstract': False,
'verbose_name': 'проверяющий',
'verbose_name_plural': 'проверяющие',
},
),
migrations.CreateModel(
name='Review',
fields=[
('id', models.UUIDField(default=uuid.uuid4, editable=False, primary_key=True, serialize=False)),
('evaluation', models.JSONField(blank=True, default=list, null=True, verbose_name='выполнение')),
('state', models.CharField(choices=[('not_checked', 'Not Checked'), ('checking', 'Checking'), ('checked', 'Checked')], default='not_checked', max_length=11, verbose_name='состояние')),
('submission', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='task.competitiontasksubmission', verbose_name='посылка')),
('reviewer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='review.reviewer', verbose_name='проверяющий')),
],
options={
'verbose_name': 'проверка',
'verbose_name_plural': 'проверки',
},
),
]
@@ -1,27 +0,0 @@
# Generated by Django 5.1.6 on 2025-03-02 06:13
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('review', '0001_initial'),
('task', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='review',
name='submission',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='reviews', to='task.competitiontasksubmission'),
),
migrations.AddField(
model_name='review',
name='reviewer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='review.reviewer'),
),
]
+23 -5
View File
@@ -5,10 +5,17 @@ from apps.task.models import CompetitionTaskSubmission
class Reviewer(BaseModel):
name = models.CharField(max_length=100)
surname = models.CharField(max_length=100)
name = models.CharField(max_length=100, verbose_name="имя")
surname = models.CharField(max_length=100, verbose_name="фамилия")
token = models.CharField(max_length=100)
token = models.CharField(max_length=100, verbose_name="токен для входа")
def __str__(self):
return self.name + " " + self.surname
class Meta:
verbose_name = "проверяющий"
verbose_name_plural = "проверяющие"
class ReviewStatusChoices(models.TextChoices):
@@ -18,16 +25,27 @@ class ReviewStatusChoices(models.TextChoices):
class Review(BaseModel):
reviewer = models.ForeignKey(Reviewer, on_delete=models.CASCADE)
reviewer = models.ForeignKey(Reviewer, on_delete=models.CASCADE,
verbose_name="проверяющий")
submission = models.ForeignKey(
CompetitionTaskSubmission,
on_delete=models.CASCADE,
related_name="reviews",
verbose_name="посылка"
)
evaluation = models.JSONField(default=list, null=True, blank=True)
evaluation = models.JSONField(default=list, null=True, blank=True,
verbose_name="выполнение")
state = models.CharField(
choices=ReviewStatusChoices.choices,
default=ReviewStatusChoices.NOT_CHECKED.value,
max_length=11,
verbose_name="состояние"
)
def __str__(self):
return self.id
class Meta:
verbose_name = "проверка"
verbose_name_plural = "проверки"
-1
View File
@@ -11,7 +11,6 @@ class CompletionAttachmentInline(admin.StackedInline):
@admin.register(CompetitionTask)
class CompetitionTaskAdmin(admin.ModelAdmin):
list_display = ("title", "type", "points")
inlines = [CompletionAttachmentInline]
class CompetitionTaskInline(admin.StackedInline):
@@ -0,0 +1,40 @@
# Generated by Django 5.1.6 on 2025-03-02 08:50
import apps.task.models
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('task', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='competitiontask',
name='attachments',
field=models.ManyToManyField(blank=True, related_name='tasks_attachments', to='task.competitiontaskattachment'),
),
migrations.AlterField(
model_name='competitiontaskattachment',
name='bind_at',
field=models.FilePathField(verbose_name='путь сохранения'),
),
migrations.AlterField(
model_name='competitiontaskattachment',
name='file',
field=models.FileField(upload_to=apps.task.models.CompetitionTaskAttachment.file_upload_at, verbose_name='файл'),
),
migrations.AlterField(
model_name='competitiontaskattachment',
name='public',
field=models.BooleanField(default=False, verbose_name='публичный'),
),
migrations.AlterField(
model_name='competitiontaskattachment',
name='task',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='task.competitiontask', verbose_name='задание'),
),
]
@@ -0,0 +1,17 @@
# Generated by Django 5.1.6 on 2025-03-02 09:31
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('task', '0002_competitiontask_attachments_and_more'),
]
operations = [
migrations.RemoveField(
model_name='competitiontask',
name='attachments',
),
]
+30 -4
View File
@@ -71,10 +71,12 @@ class CompetitionTaskAttachment(BaseModel):
def file_upload_at(instance, filename):
return f"/attachment/{instance.id}/file"
task = models.ForeignKey(CompetitionTask, on_delete=models.CASCADE)
file = models.FileField(upload_to=file_upload_at)
bind_at = models.FilePathField()
public = models.BooleanField(default=False)
task = models.ForeignKey(CompetitionTask, on_delete=models.CASCADE,
verbose_name="задание")
file = models.FileField(upload_to=file_upload_at,
verbose_name="файл")
bind_at = models.FilePathField(verbose_name="путь сохранения")
public = models.BooleanField(default=False, verbose_name="публичный")
class CompetitionTaskSubmission(BaseModel):
@@ -117,3 +119,27 @@ class CompetitionTaskSubmission(BaseModel):
checked_at = models.DateTimeField(null=True, blank=True)
plagiarism_checked = models.BooleanField(default=False)
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,
)
-1
View File
@@ -1,4 +1,3 @@
from django.db import models
from apps.core.models import BaseModel
+5921
View File
File diff suppressed because it is too large Load Diff