From 5cc4f9bddfa86e4af888bcc5979bd3df1df87684 Mon Sep 17 00:00:00 2001 From: ITQ Date: Sat, 1 Mar 2025 16:54:25 +0300 Subject: [PATCH 1/6] (scope): [body] [footer(s)] --- .../0003_remove_competition_tasks.py | 17 +++++++ ...03_competitiontask_max_attemps_and_more.py | 51 +++++++++++++++++++ services/backend/apps/task/models.py | 10 +--- 3 files changed, 69 insertions(+), 9 deletions(-) create mode 100644 services/backend/apps/competition/migrations/0003_remove_competition_tasks.py create mode 100644 services/backend/apps/task/migrations/0003_competitiontask_max_attemps_and_more.py diff --git a/services/backend/apps/competition/migrations/0003_remove_competition_tasks.py b/services/backend/apps/competition/migrations/0003_remove_competition_tasks.py new file mode 100644 index 0000000..b03500b --- /dev/null +++ b/services/backend/apps/competition/migrations/0003_remove_competition_tasks.py @@ -0,0 +1,17 @@ +# Generated by Django 5.1.6 on 2025-03-01 13:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('competition', '0002_competition_tasks_alter_competition_participants_and_more'), + ] + + operations = [ + migrations.RemoveField( + model_name='competition', + name='tasks', + ), + ] diff --git a/services/backend/apps/task/migrations/0003_competitiontask_max_attemps_and_more.py b/services/backend/apps/task/migrations/0003_competitiontask_max_attemps_and_more.py new file mode 100644 index 0000000..039cbdf --- /dev/null +++ b/services/backend/apps/task/migrations/0003_competitiontask_max_attemps_and_more.py @@ -0,0 +1,51 @@ +# Generated by Django 5.1.6 on 2025-03-01 13:49 + +import apps.task.models +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('competition', '0003_remove_competition_tasks'), + ('task', '0002_alter_competitiontask_options_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='competitiontask', + name='max_attemps', + field=models.PositiveSmallIntegerField(default=0), + ), + migrations.AlterField( + model_name='competitiontask', + name='answer_file_path', + field=models.TextField(blank=True, default='stdout', null=True, verbose_name='куда сохранять решения'), + ), + migrations.AlterField( + model_name='competitiontask', + name='competition', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='competition.competition'), + ), + migrations.AlterField( + model_name='competitiontask', + name='correct_answer_file', + field=models.FileField(blank=True, null=True, upload_to=apps.task.models.CompetitionTask.answer_file_upload_to, verbose_name='файл с правильным ответом'), + ), + migrations.AlterField( + model_name='competitiontask', + name='criteries', + field=models.JSONField(blank=True, null=True, verbose_name='критерии'), + ), + migrations.AlterField( + model_name='competitiontask', + name='title', + field=models.CharField(max_length=50, verbose_name='заголовок'), + ), + migrations.AlterField( + model_name='competitiontask', + name='type', + field=models.CharField(choices=[('input', 'Ввод правильного ответа'), ('checker', 'Вывод кода'), ('review', 'Ручная')], max_length=8, verbose_name='тип проверки'), + ), + ] diff --git a/services/backend/apps/task/models.py b/services/backend/apps/task/models.py index e03a31e..72bb349 100644 --- a/services/backend/apps/task/models.py +++ b/services/backend/apps/task/models.py @@ -20,7 +20,7 @@ class CompetitionTask(BaseModel): competition = models.ForeignKey(Competition, on_delete=models.CASCADE) title = models.CharField(verbose_name="заголовок", max_length=50) description = models.TextField(verbose_name="описание", max_length=300) - max_attemps = models.PositiveSmallIntegerField() + max_attemps = models.PositiveSmallIntegerField(default=0) type = models.CharField( choices=CompetitionTaskType, max_length=8, verbose_name="тип проверки" ) @@ -50,14 +50,6 @@ class CompetitionTask(BaseModel): blank=True, null=True, verbose_name="критерии", - default=lambda: [ - { - "name": "CHANGE ME", - "slug": "CHANGE ME", - "max_value": 0, - "min_value": 0, - } - ], ) def clean(self): From 332ca694a89ed1f3391e9ef0ff45a0323041215c 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: Sat, 1 Mar 2025 17:20:45 +0300 Subject: [PATCH 2/6] feat: added review logic --- services/backend/api/v1/review/schemas.py | 12 ++++++------ services/backend/api/v1/review/views.py | 17 ++++++++++++++++- services/backend/api/v1/task/views.py | 16 ++++++++-------- services/backend/apps/competition/models.py | 2 ++ .../core/management/commands/generate_data.py | 4 ++-- services/backend/apps/review/models.py | 12 ++++++++++++ .../apps/task/migrations/0001_initial.py | 4 ++-- ...002_competetiontasksumbission_reviewers.py | 19 +++++++++++++++++++ services/backend/apps/task/models.py | 12 ++++++++++-- 9 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 services/backend/apps/task/migrations/0002_competetiontasksumbission_reviewers.py diff --git a/services/backend/api/v1/review/schemas.py b/services/backend/api/v1/review/schemas.py index 70bf9b3..831550b 100644 --- a/services/backend/api/v1/review/schemas.py +++ b/services/backend/api/v1/review/schemas.py @@ -4,8 +4,8 @@ from uuid import UUID from django.http import HttpRequest from ninja import ModelSchema, Schema -from apps.review.models import Reviewer -from apps.task.models import CompetetionTaskSumbission +from apps.review.models import Reviewer, Review +from apps.task.models import CompetitionTaskSubmission class PingOut(Schema): @@ -25,13 +25,13 @@ class SubmissionOut(ModelSchema): status: Literal["sent", "checking", "checked"] class Meta: - model = CompetetionTaskSumbission + model = CompetitionTaskSubmission exclude = ("user",) class SubmissionsOut(Schema): - submissions: list[SubmissionOut] = [] + submissions: list = None @staticmethod - def resolve_submissions(self, context: HttpRequest) -> list[SubmissionOut]: - return list(CompetetionTaskSumbission.objects.all()) + def resolve_submissions(self, context) -> list[SubmissionOut]: + return list(Review.objects.filter(reviewer=context.get("request").auth)) \ No newline at end of file diff --git a/services/backend/api/v1/review/views.py b/services/backend/api/v1/review/views.py index d628faf..66f608e 100644 --- a/services/backend/api/v1/review/views.py +++ b/services/backend/api/v1/review/views.py @@ -1,10 +1,15 @@ +import logging from http import HTTPStatus as status +from uuid import UUID from django.http import HttpRequest +from django.shortcuts import get_object_or_404 from ninja import Router from api.v1 import schemas as global_schemas from api.v1.review import schemas +from api.v1.task.schemas import TaskSubmissionIn +from apps.task.models import CompetitionTaskSubmission router = Router(tags=["review"]) @@ -16,7 +21,7 @@ router = Router(tags=["review"]) }, 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() @@ -30,3 +35,13 @@ def get_submissions(request: HttpRequest, token) -> tuple[status, schemas.Submis ) def get_reviewer_profile(request: HttpRequest, token: str): 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 diff --git a/services/backend/api/v1/task/views.py b/services/backend/api/v1/task/views.py index 208dd36..85d3cc1 100644 --- a/services/backend/api/v1/task/views.py +++ b/services/backend/api/v1/task/views.py @@ -13,7 +13,7 @@ from api.v1.task.schemas import ( ) from apps.competition.models import State from apps.task.models import ( - CompetetionTaskSumbission, + CompetitionTaskSubmission, Competition, CompetitionTask, ) @@ -96,23 +96,23 @@ def submit_task( ) if task.type == CompetitionTask.CompetitionTaskType.INPUT: - CompetetionTaskSumbission.objects.create( + CompetitionTaskSubmission.objects.create( user=user, task=task, - status=CompetetionTaskSumbission.StatusChoices.CHECKED, + status=CompetitionTaskSubmission.StatusChoices.CHECKED, result={"correct": submission.content == task.answer_file_path}, ) if task.type == CompetitionTask.CompetitionTaskType.REVIEW: - CompetetionTaskSumbission.objects.create( + CompetitionTaskSubmission.objects.create( user=user, task=task, - status=CompetetionTaskSumbission.StatusChoices.SENT, + status=CompetitionTaskSubmission.StatusChoices.SENT, ) if task.type == CompetitionTask.CompetitionTaskType.CHECKER: - CompetetionTaskSumbission.objects.create( + CompetitionTaskSubmission.objects.create( user=user, task=task, - status=CompetetionTaskSumbission.StatusChoices.CHECKING, + status=CompetitionTaskSubmission.StatusChoices.CHECKING, ) - return TaskSubmissionOut(id=CompetetionTaskSumbission.id) + return TaskSubmissionOut(id=CompetitionTaskSubmission.id) diff --git a/services/backend/apps/competition/models.py b/services/backend/apps/competition/models.py index 0c6bd19..bd52ab7 100644 --- a/services/backend/apps/competition/models.py +++ b/services/backend/apps/competition/models.py @@ -41,6 +41,8 @@ class Competition(BaseModel): def __str__(self): return self.title + + class Meta: verbose_name = "соревнование" verbose_name_plural = "соревнования" diff --git a/services/backend/apps/core/management/commands/generate_data.py b/services/backend/apps/core/management/commands/generate_data.py index c781811..479fd23 100644 --- a/services/backend/apps/core/management/commands/generate_data.py +++ b/services/backend/apps/core/management/commands/generate_data.py @@ -8,7 +8,7 @@ from django.core.management.base import BaseCommand from django.utils import timezone 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 @@ -105,7 +105,7 @@ class Command(BaseCommand): b"Submission content", name=f"submission_{uuid.uuid4().hex}.txt", ) - submission = CompetetionTaskSumbission.objects.create( + submission = CompetitionTaskSubmission.objects.create( user=user, task=task, earned_points=random.randint( diff --git a/services/backend/apps/review/models.py b/services/backend/apps/review/models.py index 02b74af..60eb198 100644 --- a/services/backend/apps/review/models.py +++ b/services/backend/apps/review/models.py @@ -1,6 +1,7 @@ from django.db import models from apps.core.models import BaseModel +from apps.task.models import CompetitionTaskSubmission class Reviewer(BaseModel): @@ -8,3 +9,14 @@ class Reviewer(BaseModel): surname = 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) \ No newline at end of file diff --git a/services/backend/apps/task/migrations/0001_initial.py b/services/backend/apps/task/migrations/0001_initial.py index c3e32f9..e65c59e 100644 --- a/services/backend/apps/task/migrations/0001_initial.py +++ b/services/backend/apps/task/migrations/0001_initial.py @@ -38,8 +38,8 @@ class Migration(migrations.Migration): fields=[ ('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)), - ('content', models.FileField(upload_to=apps.task.models.CompetetionTaskSumbission.submission_content_upload_to)), - ('stdout', models.FileField(blank=True, null=True, upload_to=apps.task.models.CompetetionTaskSumbission.submission_stdout_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)), ('result', models.JSONField(blank=True, default=None, null=True)), ('earned_points', models.IntegerField()), ('timestamp', models.DateTimeField(auto_now_add=True)), diff --git a/services/backend/apps/task/migrations/0002_competetiontasksumbission_reviewers.py b/services/backend/apps/task/migrations/0002_competetiontasksumbission_reviewers.py new file mode 100644 index 0000000..bda15a2 --- /dev/null +++ b/services/backend/apps/task/migrations/0002_competetiontasksumbission_reviewers.py @@ -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'), + ), + ] diff --git a/services/backend/apps/task/models.py b/services/backend/apps/task/models.py index 72bb349..f9b9a4c 100644 --- a/services/backend/apps/task/models.py +++ b/services/backend/apps/task/models.py @@ -20,7 +20,7 @@ class CompetitionTask(BaseModel): competition = models.ForeignKey(Competition, on_delete=models.CASCADE) title = models.CharField(verbose_name="заголовок", max_length=50) description = models.TextField(verbose_name="описание", max_length=300) - max_attemps = models.PositiveSmallIntegerField(default=0) + max_attemps = models.PositiveSmallIntegerField() type = models.CharField( choices=CompetitionTaskType, max_length=8, verbose_name="тип проверки" ) @@ -50,6 +50,14 @@ class CompetitionTask(BaseModel): blank=True, null=True, verbose_name="критерии", + default=lambda: [ + { + "name": "CHANGE ME", + "slug": "CHANGE ME", + "max_value": 0, + "min_value": 0, + } + ], ) def clean(self): @@ -63,7 +71,7 @@ class CompetitionTask(BaseModel): verbose_name_plural = "задания" -class CompetetionTaskSumbission(BaseModel): +class CompetitionTaskSubmission(BaseModel): class StatusChoices(models.TextChoices): SENT = "sent" CHECKING = "checking" From c0d4c5673696db9d3016c2afe63c26ede3e1b407 Mon Sep 17 00:00:00 2001 From: Timur Date: Sat, 1 Mar 2025 17:23:12 +0300 Subject: [PATCH 3/6] add tinymce --- services/backend/apps/competition/models.py | 15 ++++++++------- services/backend/apps/task/models.py | 3 ++- services/backend/config/settings.py | 17 +++++++++++++++++ services/backend/config/urls.py | 2 ++ services/backend/pyproject.toml | 1 + 5 files changed, 30 insertions(+), 8 deletions(-) diff --git a/services/backend/apps/competition/models.py b/services/backend/apps/competition/models.py index bd52ab7..f9d91ec 100644 --- a/services/backend/apps/competition/models.py +++ b/services/backend/apps/competition/models.py @@ -1,6 +1,7 @@ from datetime import datetime from django.db import models +from tinymce.models import HTMLField from apps.core.models import BaseModel from apps.user.models import User @@ -14,26 +15,26 @@ class Competition(BaseModel): EDU = "edu", "Образовательный" COMPETITIVE = "competitive", "Соревновательный" - title = models.CharField(max_length=100, verbose_name="Название") - description = models.TextField(verbose_name="Описание") + title = models.CharField(max_length=100, verbose_name="аазвание") + description = HTMLField(verbose_name="описание") image_url = models.FileField( - verbose_name="Изображение соревнования", null=True, blank=True + verbose_name="изображение соревнования", null=True, blank=True ) end_date = models.DateTimeField( - verbose_name="Дедлайн участия", null=True, blank=True + verbose_name="дедлайн участия", null=True, blank=True ) start_date = models.DateTimeField( - verbose_name="Дедлайн участия", null=True, blank=True + verbose_name="дедлайн участия", null=True, blank=True ) type = models.CharField( max_length=10, choices=CompetitionType.choices, - verbose_name="Тип участия", + verbose_name="тип участия", ) participation_type = models.CharField( max_length=11, choices=CompetitionParticipationType.choices, - verbose_name="Тип соревнования", + verbose_name="тип соревнования", ) participants = models.ManyToManyField(User, related_name="participants", blank=True, editable=False) diff --git a/services/backend/apps/task/models.py b/services/backend/apps/task/models.py index f9b9a4c..13dd487 100644 --- a/services/backend/apps/task/models.py +++ b/services/backend/apps/task/models.py @@ -1,6 +1,7 @@ from uuid import uuid4 from django.db import models +from tinymce.models import HTMLField from apps.competition.models import Competition from apps.core.models import BaseModel @@ -19,7 +20,7 @@ class CompetitionTask(BaseModel): competition = models.ForeignKey(Competition, on_delete=models.CASCADE) title = models.CharField(verbose_name="заголовок", max_length=50) - description = models.TextField(verbose_name="описание", max_length=300) + description = HTMLField(verbose_name="описание", max_length=300) max_attemps = models.PositiveSmallIntegerField() type = models.CharField( choices=CompetitionTaskType, max_length=8, verbose_name="тип проверки" diff --git a/services/backend/config/settings.py b/services/backend/config/settings.py index e383e80..af06a21 100644 --- a/services/backend/config/settings.py +++ b/services/backend/config/settings.py @@ -441,6 +441,7 @@ INSTALLED_APPS = [ "django_guid", "ninja", "minio_storage", + "tinymce", # Internal apps "apps.core", "apps.user", @@ -449,6 +450,22 @@ INSTALLED_APPS = [ "apps.task", ] +# tinymce +TINYMCE_DEFAULT_CONFIG = { + "theme": "silver", + "height": 500, + "menubar": False, + "plugins": "advlist,autolink,lists,link,image,charmap,print,preview,anchor," + "searchreplace,visualblocks,code,fullscreen,insertdatetime,media,table,paste," + "code,help,wordcount", + "toolbar": "undo redo | formatselect | " + "bold italic backcolor | alignleft aligncenter " + "alignright alignjustify | bullist numlist outdent indent | " + "removeformat | help", + "skin": "oxide-dark", + "content_css": "dark" +} + # GUID DJANGO_GUID = { diff --git a/services/backend/config/urls.py b/services/backend/config/urls.py index 264fcc1..f69ade8 100644 --- a/services/backend/config/urls.py +++ b/services/backend/config/urls.py @@ -12,6 +12,8 @@ admin.site.index_title = "DataRush" urlpatterns = [ + # tinymce + path('tinymce/', include('tinymce.urls')), # Admin urls path("admin/", admin.site.urls), # API urls diff --git a/services/backend/pyproject.toml b/services/backend/pyproject.toml index b6292e0..a3c2a81 100644 --- a/services/backend/pyproject.toml +++ b/services/backend/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "django-health-check>=3.18.3", "django-minio-storage>=0.5.7", "django-ninja>=1.3.0", + "django-pagedown>=2.2.1", "django-stubs-ext>=5.1.3", "gunicorn>=23.0.0", "httpx>=0.28.1", From 9a5a924333c4261d6139781bac3533ad2f74780e Mon Sep 17 00:00:00 2001 From: Timur Date: Sat, 1 Mar 2025 17:23:37 +0300 Subject: [PATCH 4/6] make verbose names at user lowercased --- services/backend/apps/user/models.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/services/backend/apps/user/models.py b/services/backend/apps/user/models.py index a5b19fc..bc7ce07 100644 --- a/services/backend/apps/user/models.py +++ b/services/backend/apps/user/models.py @@ -9,9 +9,9 @@ class UserRole(models.Choices): class User(BaseModel): - email = models.EmailField(unique=True, verbose_name="Почта") - username = models.SlugField(unique=True, verbose_name="Юзернейм") - password = models.TextField(verbose_name="Пароль") + email = models.EmailField(unique=True, verbose_name="почта") + username = models.SlugField(unique=True, verbose_name="юзернейм") + password = models.TextField(verbose_name="пароль") status = models.CharField( max_length=10, choices=UserRole, default="student" From 656735649db681f26e7a8e4788ef0931cc61b3e1 Mon Sep 17 00:00:00 2001 From: Timur Date: Sat, 1 Mar 2025 17:40:08 +0300 Subject: [PATCH 5/6] add simple user admin and hash password --- services/backend/api/v1/user/views.py | 3 ++- services/backend/apps/user/admin.py | 9 +++++++++ services/backend/apps/user/apps.py | 1 + services/backend/apps/user/models.py | 9 ++++++++- services/backend/config/settings.py | 4 ++++ services/backend/pyproject.toml | 1 + 6 files changed, 25 insertions(+), 2 deletions(-) create mode 100644 services/backend/apps/user/admin.py diff --git a/services/backend/api/v1/user/views.py b/services/backend/api/v1/user/views.py index ff49988..c4f8c15 100644 --- a/services/backend/api/v1/user/views.py +++ b/services/backend/api/v1/user/views.py @@ -27,6 +27,7 @@ router = Router(tags=["user"]) ) def sign_up(request, data: RegisterSchema): user = User(**data.dict()) + user.password = user.make_password() user.full_clean() user.save() @@ -47,7 +48,7 @@ def sign_in(request, data: LoginSchema): user = User.objects.filter(email=data.email).first() if not user: raise AuthenticationError - if user.password != data.password: + if not user.check_password(data.password): raise AuthenticationError token = BearerAuth.generate_jwt(user) diff --git a/services/backend/apps/user/admin.py b/services/backend/apps/user/admin.py new file mode 100644 index 0000000..89dca07 --- /dev/null +++ b/services/backend/apps/user/admin.py @@ -0,0 +1,9 @@ +from django.contrib import admin + +from apps.user.models import User + + +@admin.register(User) +class UserAdmin(admin.ModelAdmin): + list_display = ("email", "username") + search_fields = ("id", "email", "username") diff --git a/services/backend/apps/user/apps.py b/services/backend/apps/user/apps.py index 2f3daa6..dd71f2d 100644 --- a/services/backend/apps/user/apps.py +++ b/services/backend/apps/user/apps.py @@ -5,3 +5,4 @@ class UsersConfig(AppConfig): default_auto_field = "django.db.models.BigAutoField" name = "apps.user" label = "user" + verbose_name = "Пользователи" diff --git a/services/backend/apps/user/models.py b/services/backend/apps/user/models.py index bc7ce07..8fa8b44 100644 --- a/services/backend/apps/user/models.py +++ b/services/backend/apps/user/models.py @@ -1,4 +1,5 @@ from django.db import models +from django.contrib.auth.hashers import check_password, make_password from apps.core.models import BaseModel @@ -11,7 +12,13 @@ class UserRole(models.Choices): class User(BaseModel): email = models.EmailField(unique=True, verbose_name="почта") username = models.SlugField(unique=True, verbose_name="юзернейм") - password = models.TextField(verbose_name="пароль") + password = models.TextField(verbose_name="пароль", editable=False) + + def make_password(self): + return make_password(self.password) + + def check_password(self, password): + return check_password(self.password, password) status = models.CharField( max_length=10, choices=UserRole, default="student" diff --git a/services/backend/config/settings.py b/services/backend/config/settings.py index af06a21..dbb0717 100644 --- a/services/backend/config/settings.py +++ b/services/backend/config/settings.py @@ -483,6 +483,10 @@ DJANGO_GUID = { LANGUAGE_COOKIE_AGE = 31449600 +PASSWORD_HASHERS = [ + "django.contrib.auth.hashers.Argon2PasswordHasher", +] + LANGUAGE_COOKIE_DOMAIN = None LANGUAGE_COOKIE_HTTPONLY = False diff --git a/services/backend/pyproject.toml b/services/backend/pyproject.toml index a3c2a81..200a90d 100644 --- a/services/backend/pyproject.toml +++ b/services/backend/pyproject.toml @@ -4,6 +4,7 @@ version = "0.1.0" readme = "README.md" requires-python = ">=3.10,<3.12" dependencies = [ + "argon2-cffi>=23.1.0", "celery>=5.4.0", "colorlog>=6.9.0", "django-cors-headers>=4.6.0", From c4aecb26b5fe3ae17004f378d0a039622cf57f16 Mon Sep 17 00:00:00 2001 From: Timur Date: Sat, 1 Mar 2025 17:48:27 +0300 Subject: [PATCH 6/6] show password in admin and fix migrations generator --- services/backend/apps/task/models.py | 8 -------- services/backend/apps/user/models.py | 2 +- 2 files changed, 1 insertion(+), 9 deletions(-) diff --git a/services/backend/apps/task/models.py b/services/backend/apps/task/models.py index 13dd487..3373809 100644 --- a/services/backend/apps/task/models.py +++ b/services/backend/apps/task/models.py @@ -51,14 +51,6 @@ class CompetitionTask(BaseModel): blank=True, null=True, verbose_name="критерии", - default=lambda: [ - { - "name": "CHANGE ME", - "slug": "CHANGE ME", - "max_value": 0, - "min_value": 0, - } - ], ) def clean(self): diff --git a/services/backend/apps/user/models.py b/services/backend/apps/user/models.py index 8fa8b44..f702b6f 100644 --- a/services/backend/apps/user/models.py +++ b/services/backend/apps/user/models.py @@ -12,7 +12,7 @@ class UserRole(models.Choices): class User(BaseModel): email = models.EmailField(unique=True, verbose_name="почта") username = models.SlugField(unique=True, verbose_name="юзернейм") - password = models.TextField(verbose_name="пароль", editable=False) + password = models.TextField(verbose_name="пароль") def make_password(self): return make_password(self.password)