fix: fixed admin bug and added 12rojgpouehgsrfojn

This commit is contained in:
Андрей Сумин
2025-03-02 13:34:54 +03:00
parent f95d6c1c6c
commit 5e8742ee10
13 changed files with 75 additions and 140 deletions
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-02 06:13 # Generated by Django 5.1.6 on 2025-03-02 10:28
import apps.competition.models import apps.competition.models
import datetime import datetime
@@ -20,8 +20,8 @@ class Command(BaseCommand):
self.stdout.write("Starting data generation...") self.stdout.write("Starting data generation...")
users = self.create_users(5) users = self.create_users(5)
competitions = self.create_competitions(2, users) competitions = self.create_competitions(2, users)
self.reviewers = self.create_reviewers(2)
tasks = self.create_tasks(competitions) tasks = self.create_tasks(competitions)
self.reviewers = self.create_reviewers(1)
self.create_submissions(tasks, users) self.create_submissions(tasks, users)
self.create_states(competitions, users) self.create_states(competitions, users)
self.stdout.write("Data generation completed.") self.stdout.write("Data generation completed.")
@@ -108,8 +108,14 @@ class Command(BaseCommand):
) )
tasks.append(task) tasks.append(task)
self.stdout.write(f"Created task: {title} (type: {task_type})") self.stdout.write(f"Created task: {title} (type: {task_type})")
self.add_reviewers_to_task(tasks)
return tasks return tasks
def add_reviewers_to_task(self, tasks):
for task in tasks:
task.reviewers.set(self.reviewers)
task.save()
def create_submissions(self, tasks, users): def create_submissions(self, tasks, users):
for task in tasks: for task in tasks:
# Each task will get between 1 and 3 submissions # Each task will get between 1 and 3 submissions
@@ -133,15 +139,6 @@ class Command(BaseCommand):
self.stdout.write( self.stdout.write(
f"Created submission for task '{task.title}' by user '{user.username}'" f"Created submission for task '{task.title}' by user '{user.username}'"
) )
self.add_reviewers(submission)
def add_reviewers(self, submission):
for reviewer in self.reviewers:
if random.choice([True, False]):
Review.objects.create(
submission=submission,
reviewer=reviewer,
)
def create_states(self, competitions, users): def create_states(self, competitions, users):
# For each competition, create a State for some of its participants # For each competition, create a State for some of its participants
@@ -1,6 +1,5 @@
# Generated by Django 5.1.6 on 2025-03-02 09:31 # Generated by Django 5.1.6 on 2025-03-02 10:28
import django.db.models.deletion
import uuid import uuid
from django.db import migrations, models from django.db import migrations, models
@@ -10,10 +9,21 @@ class Migration(migrations.Migration):
initial = True initial = True
dependencies = [ dependencies = [
('task', '0003_remove_competitiontask_attachments'),
] ]
operations = [ 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, verbose_name='выполнение')),
('state', models.CharField(choices=[('not_checked', 'Not Checked'), ('checking', 'Checking'), ('checked', 'Checked')], default='not_checked', max_length=11, verbose_name='состояние')),
],
options={
'verbose_name': 'проверка',
'verbose_name_plural': 'проверки',
},
),
migrations.CreateModel( migrations.CreateModel(
name='Reviewer', name='Reviewer',
fields=[ fields=[
@@ -27,18 +37,4 @@ class Migration(migrations.Migration):
'verbose_name_plural': 'проверяющие', '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': 'проверки',
},
),
] ]
@@ -0,0 +1,27 @@
# Generated by Django 5.1.6 on 2025-03-02 10:28
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', verbose_name='посылка'),
),
migrations.AddField(
model_name='review',
name='reviewer',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='review.reviewer', verbose_name='проверяющий'),
),
]
+1 -1
View File
@@ -27,7 +27,7 @@ class Review(BaseModel):
reviewer = models.ForeignKey(Reviewer, on_delete=models.CASCADE, reviewer = models.ForeignKey(Reviewer, on_delete=models.CASCADE,
verbose_name="проверяющий") verbose_name="проверяющий")
submission = models.ForeignKey( submission = models.ForeignKey(
"CompetitionTaskSubmission", "task.CompetitionTaskSubmission",
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="reviews", related_name="reviews",
verbose_name="посылка" verbose_name="посылка"
+1 -1
View File
@@ -19,7 +19,7 @@ class CompetitionTaskSubmissionAdmin(admin.ModelAdmin):
list_display = ("task", "user", "status",) list_display = ("task", "user", "status",)
search_fields = ("task__id", "task__title", "user__username", "user__email") search_fields = ("task__id", "task__title", "user__username", "user__email")
filter = ("plagiarism_checked",) filter = ("plagiarism_checked",)
ordering = "-timestamp" ordering = ["-timestamp"]
def has_add_permission(self, request, obj=None): def has_add_permission(self, request, obj=None):
return False return False
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-02 06:13 # Generated by Django 5.1.6 on 2025-03-02 10:28
import apps.task.models import apps.task.models
import django.db.models.deletion import django.db.models.deletion
@@ -13,6 +13,7 @@ class Migration(migrations.Migration):
dependencies = [ dependencies = [
('competition', '0001_initial'), ('competition', '0001_initial'),
('review', '0001_initial'),
('user', '0001_initial'), ('user', '0001_initial'),
] ]
@@ -30,6 +31,7 @@ class Migration(migrations.Migration):
('points', models.IntegerField(blank=True, null=True, verbose_name='баллы за задание')), ('points', models.IntegerField(blank=True, null=True, verbose_name='баллы за задание')),
('answer_file_path', models.TextField(blank=True, default='stdout', help_text='Путь до файла в котором ожидается результат. Пример: stdout или ./output.txt', null=True, verbose_name='куда сделать вывод программы участнику')), ('answer_file_path', models.TextField(blank=True, default='stdout', help_text='Путь до файла в котором ожидается результат. Пример: stdout или ./output.txt', null=True, verbose_name='куда сделать вывод программы участнику')),
('competition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.competition')), ('competition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.competition')),
('reviewers', models.ManyToManyField(blank=True, to='review.reviewer')),
], ],
options={ options={
'verbose_name': 'задание', 'verbose_name': 'задание',
@@ -40,10 +42,10 @@ class Migration(migrations.Migration):
name='CompetitionTaskAttachment', name='CompetitionTaskAttachment',
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)),
('file', models.FileField(upload_to=apps.task.models.CompetitionTaskAttachment.file_upload_at)), ('file', models.FileField(upload_to=apps.task.models.CompetitionTaskAttachment.file_upload_at, verbose_name='файл')),
('bind_at', models.FilePathField()), ('bind_at', models.FilePathField(verbose_name='путь сохранения')),
('public', models.BooleanField(default=False)), ('public', models.BooleanField(default=False, verbose_name='публичный')),
('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='task.competitiontask')), ('task', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='task.competitiontask', verbose_name='задание')),
], ],
options={ options={
'abstract': False, 'abstract': False,
@@ -67,7 +69,7 @@ class Migration(migrations.Migration):
name='CompetitionTaskSubmission', name='CompetitionTaskSubmission',
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, verbose_name='статус')),
('content', models.FileField(upload_to=apps.task.models.CompetitionTaskSubmission.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.CompetitionTaskSubmission.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)),
@@ -1,40 +0,0 @@
# 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='задание'),
),
]
@@ -1,17 +0,0 @@
# 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',
),
]
+14 -27
View File
@@ -6,7 +6,7 @@ from tinymce.models import HTMLField
from apps.competition.models import Competition from apps.competition.models import Competition
from apps.core.models import BaseModel from apps.core.models import BaseModel
from apps.review.models import Review, ReviewStatusChoices from apps.review.models import Review, ReviewStatusChoices, Reviewer
from apps.user.models import User from apps.user.models import User
@@ -50,6 +50,9 @@ class CompetitionTask(BaseModel):
default="stdout", default="stdout",
) )
# only when "review" type
reviewers = models.ManyToManyField(Reviewer, blank=True)
def __str__(self): def __str__(self):
return self.title return self.title
@@ -93,10 +96,8 @@ class CompetitionTaskSubmission(BaseModel):
def submission_stdout_upload_to(instance, filename) -> str: def submission_stdout_upload_to(instance, filename) -> str:
return f"/submissions/{instance.id}/stdout" return f"/submissions/{instance.id}/stdout"
user = models.ForeignKey(User, on_delete=models.CASCADE, user = models.ForeignKey(User, on_delete=models.CASCADE)
verbose_name="пользователь") task = models.ForeignKey(CompetitionTask, on_delete=models.CASCADE)
task = models.ForeignKey(CompetitionTask, on_delete=models.CASCADE,
verbose_name="задание")
status = models.CharField( status = models.CharField(
choices=StatusChoices.choices, choices=StatusChoices.choices,
@@ -106,38 +107,24 @@ class CompetitionTaskSubmission(BaseModel):
) )
# code or text or file # code or text or file
content = models.FileField(upload_to=submission_content_upload_to, content = models.FileField(upload_to=submission_content_upload_to)
verbose_name="код/файл посылки")
# only if task type is checker # only if task type is checker
stdout = models.FileField( stdout = models.FileField(
upload_to=submission_stdout_upload_to, null=True, blank=True, upload_to=submission_stdout_upload_to, null=True, blank=True
verbose_name="вывод чекера"
) )
# depends on task type: # depends on task type:
# - input: {"correct": boolean} # - input: {"correct": boolean}
# - file: {"total_points": integer, "by_criteria": ["criteria_name": integer]} # - file: {"total_points": integer, "by_criteria": ["criteria_name": integer]}
# - code: {"correct": boolean} # - code: {"correct": boolean}
result = models.JSONField(default=None, null=True, blank=True, result = models.JSONField(default=None, null=True, blank=True)
verbose_name="результат проверки")
# just more readable result representation, maybe will be calcuated somehow more complex depends on criteria # just more readable result representation, maybe will be calcuated somehow more complex depends on criteria
earned_points = models.IntegerField(null=True, blank=True, earned_points = models.IntegerField(null=True, blank=True)
verbose_name="получено баллов")
checked_at = models.DateTimeField(null=True, blank=True, checked_at = models.DateTimeField(null=True, blank=True)
verbose_name="дата и время проверки") plagiarism_checked = models.BooleanField(default=False)
plagiarism_checked = models.BooleanField(default=False, timestamp = models.DateTimeField(auto_now_add=True)
verbose_name="проверено на плагиат")
timestamp = models.DateTimeField(auto_now_add=True,
verbose_name="дата отправки")
def __str__(self):
return str(self.id)
class Meta:
verbose_name = "посылка"
verbose_name_plural = "посылки"
def send_on_review(self): def send_on_review(self):
if not self.task.reviewers.exists(): if not self.task.reviewers.exists():
@@ -158,7 +145,7 @@ class CompetitionTaskSubmission(BaseModel):
.order_by("pending_count") .order_by("pending_count")
.first() .first()
) )
Review.objects.create( review = Review.objects.create(
reviewer=reviewer, reviewer=reviewer,
submission=self, submission=self,
) )
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-02 06:13 # Generated by Django 5.1.6 on 2025-03-02 10:28
import django.db.models.deletion import django.db.models.deletion
import uuid import uuid
@@ -1,4 +1,4 @@
# Generated by Django 5.1.6 on 2025-03-02 00:16 # Generated by Django 5.1.6 on 2025-03-02 10:28
import uuid import uuid
from django.db import migrations, models from django.db import migrations, models
@@ -19,6 +19,7 @@ class Migration(migrations.Migration):
('email', models.EmailField(max_length=254, unique=True, verbose_name='почта')), ('email', models.EmailField(max_length=254, unique=True, verbose_name='почта')),
('username', models.SlugField(unique=True, verbose_name='юзернейм')), ('username', models.SlugField(unique=True, verbose_name='юзернейм')),
('password', models.TextField(verbose_name='пароль')), ('password', models.TextField(verbose_name='пароль')),
('created_at', models.DateTimeField(auto_now=True)),
('status', models.CharField(choices=[('student', 'Student'), ('metodist', 'Metodist')], default='student', max_length=10)), ('status', models.CharField(choices=[('student', 'Student'), ('metodist', 'Metodist')], default='student', max_length=10)),
], ],
options={ options={
@@ -1,18 +0,0 @@
# Generated by Django 5.1.6 on 2025-03-02 09:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('user', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='user',
name='created_at',
field=models.DateTimeField(auto_now=True),
),
]