From 4a18c2a99fa777b6740cbf74c56ce05ac1ed4930 Mon Sep 17 00:00:00 2001 From: DarKylX Date: Tue, 20 Oct 2026 12:30:04 +0300 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B5=D0=B4=D0=B8=D0=BD=D0=BE=D0=B5=20?= =?UTF-8?q?=D0=BD=D0=B0=D0=BF=D0=B8=D1=81=D0=B0=D0=BD=D0=B8=D0=B5=20=D1=82?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D1=82=D0=BE=D0=B2=20=D0=B8=D0=BD=D1=82=D0=B5?= =?UTF-8?q?=D1=80=D1=84=D0=B5=D0=B9=D1=81=D0=B0=20=D1=87=D0=B5=D1=80=D0=B5?= =?UTF-8?q?=D0=B7=20=D0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 10 ++++++++++ courses/services/export_course_results.py | 4 ++-- courses/tests/test_export.py | 6 +++--- partner_programs/management_views.py | 6 +++--- .../migrations/0026_interface_copy.py | 20 +++++++++++++++++++ partner_programs/models.py | 4 ++-- partner_programs/registration_models.py | 2 +- partner_programs/serializers/attention.py | 2 +- partner_programs/serializers/management.py | 2 +- partner_programs/serializers/programs.py | 2 +- partner_programs/services/case_fields.py | 2 +- partner_programs/services/management_form.py | 2 +- .../services/native_registration.py | 8 ++++---- partner_programs/services/project_apply.py | 2 +- partner_programs/services/project_fields.py | 2 +- .../tests/test_attention_analytics_api.py | 2 +- .../tests/test_program_project_submit.py | 2 +- partner_programs/tests/test_project_apply.py | 2 +- project_rates/notifications.py | 2 +- project_rates/services.py | 4 ++-- project_rates/validators.py | 4 ++-- project_rates/workspace.py | 4 ++-- projects/admin.py | 4 ++-- projects/migrations/0035_interface_copy.py | 19 ++++++++++++++++++ projects/models.py | 8 ++++---- projects/permissions.py | 6 +++--- projects/serializers.py | 6 +++--- projects/team_service.py | 4 ++-- projects/views.py | 14 ++++++------- templates/email/expert_invitation.html | 4 ++-- templates/email/expert_invitation.txt | 2 +- templates/email/expert_joined.html | 2 +- templates/email/expert_joined.txt | 2 +- templates/email/organizer_invitation.html | 2 +- templates/email/organizer_invitation.txt | 2 +- users/admin.py | 2 +- vacancy/mapping.py | 8 ++++---- 37 files changed, 114 insertions(+), 65 deletions(-) create mode 100644 AGENTS.md create mode 100644 partner_programs/migrations/0026_interface_copy.py create mode 100644 projects/migrations/0035_interface_copy.py diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..6ee28eaa --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,10 @@ +# Тексты интерфейса PROCOLLAB + +Во всех новых и изменяемых текстах интерфейса используйте Е/е вместо U+0401/U+0451. +Правило относится к подписям, подсказкам, статусам, ошибкам, уведомлениям, +письмам, заголовкам выгрузок, Django admin и макетам Figma. + +Не переписывайте пользовательские данные, имена и собственные названия из +справочников. Не меняйте валидаторы допустимых букв, поисковую нормализацию, +идентификаторы и исторические миграции ради этого правила. Проверяйте именно +текст продукта; глобальная подмена символов в данных во время отображения запрещена. diff --git a/courses/services/export_course_results.py b/courses/services/export_course_results.py index 734b5aad..27fb6655 100644 --- a/courses/services/export_course_results.py +++ b/courses/services/export_course_results.py @@ -175,7 +175,7 @@ def _format_stage( published_lessons: list[CourseLesson], ) -> str: if course_progress.status == ProgressStatus.COMPLETED: - return "Курс завершён" + return "Курс завершен" lesson_progress_by_lesson_id = { progress.lesson_id: progress for progress in lesson_progresses @@ -212,7 +212,7 @@ def _format_stage( return _task_header(published_tasks[0]) return _lesson_stage_header(lesson) - return "Этап не определён" + return "Этап не определен" def _format_answer_cell(answer: UserTaskAnswer | None) -> str: diff --git a/courses/tests/test_export.py b/courses/tests/test_export.py index b4d307c2..f2bfd1de 100644 --- a/courses/tests/test_export.py +++ b/courses/tests/test_export.py @@ -204,7 +204,7 @@ def test_course_admin_export_marks_completed_course_stage(self): self.assertEqual(response.status_code, 200) self.assertEqual(len(rows), 2) self.assertEqual(rows[1][2], 100) - self.assertEqual(rows[1][5], "Курс завершён") + self.assertEqual(rows[1][5], "Курс завершен") self.assertEqual(rows[1][6], "Retention отражает возврат пользователей.") def test_course_admin_export_resolves_next_stage_for_not_started_next_module(self): @@ -375,7 +375,7 @@ def test_course_admin_export_is_undefined_when_all_published_lessons_are_complet percent=100, ) - self.assertEqual(self._stage_from_export(course), "Этап не определён") + self.assertEqual(self._stage_from_export(course), "Этап не определен") def test_course_admin_export_is_undefined_when_course_has_no_published_lessons(self): course = create_course(title="Экспорт без опубликованных уроков") @@ -395,7 +395,7 @@ def test_course_admin_export_is_undefined_when_course_has_no_published_lessons(s self._mark_course_started(student, course) - self.assertEqual(self._stage_from_export(course), "Этап не определён") + self.assertEqual(self._stage_from_export(course), "Этап не определен") def test_course_admin_export_skips_draft_modules_in_stage_fallback(self): course = create_course(title="Экспорт пропуска draft модуля") diff --git a/partner_programs/management_views.py b/partner_programs/management_views.py index 6ae99746..2168a648 100644 --- a/partner_programs/management_views.py +++ b/partner_programs/management_views.py @@ -203,7 +203,7 @@ def patch(self, request, pk, section): ): return conflict( "reopen_confirmation_required", - "Изменение срока снова откроет приём или оценивание. Подтвердите сохранение.", + "Изменение срока снова откроет прием или оценивание. Подтвердите сохранение.", ) program.save(update_fields=[*values, "datetime_updated"]) return Response(management_data(program)) @@ -243,7 +243,7 @@ def patch(self, request, pk, material_id): if values.pop("expected_updated_at") != material.datetime_updated: return conflict( "stale_material", - "Материал изменён другим организатором. Обновите список.", + "Материал изменен другим организатором. Обновите список.", ) for key, value in values.items(): setattr(material, key, value) @@ -258,7 +258,7 @@ def delete(self, request, pk, material_id): if expected != material.datetime_updated: return conflict( "stale_material", - "Материал изменён другим организатором. Обновите список.", + "Материал изменен другим организатором. Обновите список.", ) # Удаляется только материал программы; исходный UserFile остаётся доступен владельцу. material.delete() diff --git a/partner_programs/migrations/0026_interface_copy.py b/partner_programs/migrations/0026_interface_copy.py new file mode 100644 index 00000000..c2b3541a --- /dev/null +++ b/partner_programs/migrations/0026_interface_copy.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.11 on 2026-10-20 09:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('files', '0007_auto_20230929_1727'), + ('partner_programs', '0025_registration_connection_details'), + ] + + operations = [ + migrations.AlterField( + model_name='partnerprogrammaterial', + name='file', + field=models.ForeignKey(blank=True, help_text='Если указан файл, ссылка берется из него', null=True, on_delete=django.db.models.deletion.SET_NULL, to='files.userfile', verbose_name='Файл'), + ), + ] diff --git a/partner_programs/models.py b/partner_programs/models.py index 0f37a666..53571857 100644 --- a/partner_programs/models.py +++ b/partner_programs/models.py @@ -373,7 +373,7 @@ class PartnerProgramMaterial(models.Model): blank=True, null=True, verbose_name="Файл", - help_text="Если указан файл, ссылка берётся из него", + help_text="Если указан файл, ссылка берется из него", ) datetime_created = models.DateTimeField(auto_now_add=True) @@ -445,7 +445,7 @@ def clean(self): "invalid_submission", "Программа не является конкурсной." ) if not self.partner_program.is_project_submission_open(): - raise TeamError("submission_closed", "Срок подачи проектов завершён.") + raise TeamError("submission_closed", "Срок подачи проектов завершен.") team_policy.validate_submission_team(self.project, self) from django.db import connection from partner_programs.services.case_fields import ( diff --git a/partner_programs/registration_models.py b/partner_programs/registration_models.py index 456c29cd..e99bc55f 100644 --- a/partner_programs/registration_models.py +++ b/partner_programs/registration_models.py @@ -37,7 +37,7 @@ class ProgramRegistration(models.Model): def clean(self): super().clean() if self.external_status == "connected" and not self.external_url: - raise ValidationError({"external_url": "Укажите адрес подключённой формы."}) + raise ValidationError({"external_url": "Укажите адрес подключенной формы."}) def __str__(self): return f"Регистрация программы {self.program_id}" diff --git a/partner_programs/serializers/attention.py b/partner_programs/serializers/attention.py index 6a428d82..1a80d5f3 100644 --- a/partner_programs/serializers/attention.py +++ b/partner_programs/serializers/attention.py @@ -80,7 +80,7 @@ class ProgramNotSubmittedMetadataSerializer(serializers.Serializer): WAITING_REASONS = { "no_assignments": "Эксперты не назначены", - "no_completed_evaluations": "Нет завершённых оценок", + "no_completed_evaluations": "Нет завершенных оценок", "partially_evaluated": "Частично оценено", "awaiting_first_evaluation": "Ожидает первой оценки", } diff --git a/partner_programs/serializers/management.py b/partner_programs/serializers/management.py index bfbbf522..ee5fd7e6 100644 --- a/partner_programs/serializers/management.py +++ b/partner_programs/serializers/management.py @@ -170,7 +170,7 @@ def get_fields(self): def validate(self, attrs): editable = BASIC_FIELDS if self.context["section"] == "basic" else DATE_FIELDS if not any(key in attrs for key in editable): - raise serializers.ValidationError("Нет изменённых полей.") + raise serializers.ValidationError("Нет измененных полей.") return attrs class Meta: diff --git a/partner_programs/serializers/programs.py b/partner_programs/serializers/programs.py index 78bfbf8d..8f6bd758 100644 --- a/partner_programs/serializers/programs.py +++ b/partner_programs/serializers/programs.py @@ -379,7 +379,7 @@ class ProgramProjectFilterRequestSerializer(serializers.Serializer): def validate_filters(self, value): if not isinstance(value, dict): raise serializers.ValidationError( - "Поле filters должно быть объектом (словарём ключ-значение)" + "Поле filters должно быть объектом (словарем ключ-значение)" ) if len(value) > self.MAX_FILTERS: diff --git a/partner_programs/services/case_fields.py b/partner_programs/services/case_fields.py index 42e25950..7946e930 100644 --- a/partner_programs/services/case_fields.py +++ b/partner_programs/services/case_fields.py @@ -57,7 +57,7 @@ def validate_program_case_configuration(field): elif len({value.casefold() for value in options}) != len(options): errors[ "options" - ] = "Варианты кейсов не должны повторяться (без учёта регистра и внешних пробелов)." + ] = "Варианты кейсов не должны повторяться (без учета регистра и внешних пробелов)." if field.pk: used = set(field.values.values_list("value_text", flat=True).distinct()) if used - set(options): diff --git a/partner_programs/services/management_form.py b/partner_programs/services/management_form.py index 8098504c..b5ce8e18 100644 --- a/partner_programs/services/management_form.py +++ b/partner_programs/services/management_form.py @@ -156,7 +156,7 @@ def save_form(program_id, user, payload): if pk not in ids: if field.values.filter(value_text__regex=r"\S").exists(): raise ValidationError( - {"fields": "Нельзя удалить поле с сохранёнными ответами."} + {"fields": "Нельзя удалить поле с сохраненными ответами."} ) field.delete() for item in data["fields"]: diff --git a/partner_programs/services/native_registration.py b/partner_programs/services/native_registration.py index 451a06f6..2c028575 100644 --- a/partner_programs/services/native_registration.py +++ b/partner_programs/services/native_registration.py @@ -207,7 +207,7 @@ def open_program(pk, *, lock=False): def published_config(program): config = config_for(program) if not config or not config.published or config.published["mode"] != "native": - raise ValidationError("Встроенная регистрация ещё не опубликована.") + raise ValidationError("Встроенная регистрация еще не опубликована.") return config @@ -315,7 +315,7 @@ def current_answers(program, data): config = published_config(program) if config.published_version != data["version"]: raise WorkspaceConflict( - "form_changed", "Анкета обновлена. Проверьте ответы и отправьте её ещё раз." + "form_changed", "Анкета обновлена. Проверьте ответы и отправьте ее еще раз." ) return config, validate_answers(config.published, data["answers"]) @@ -346,7 +346,7 @@ def create_enrollment(program, user, answers, schema): @transaction.atomic def enroll(pk, user, data): if not user.is_authenticated or not user.is_active: - raise PermissionDenied("Войдите в подтверждённый аккаунт.") + raise PermissionDenied("Войдите в подтвержденный аккаунт.") program = open_program(pk, lock=True) if PartnerProgramUserProfile.objects.filter( partner_program=program, user=user @@ -422,7 +422,7 @@ def confirm(pk, token): except signing.BadSignature: raise WorkspaceConflict( "confirmation_expired", - "Ссылка недействительна или истекла. Отправьте письмо ещё раз.", + "Ссылка недействительна или истекла. Отправьте письмо еще раз.", ) program = open_program(pk, lock=True) pending = get_object_or_404( diff --git a/partner_programs/services/project_apply.py b/partner_programs/services/project_apply.py index 1768cb49..f7f0a192 100644 --- a/partner_programs/services/project_apply.py +++ b/partner_programs/services/project_apply.py @@ -38,7 +38,7 @@ def require_can_apply_project_to_program( user: User, ) -> None: if not program.is_project_submission_open(): - raise ValidationError("Срок подачи проектов в программу завершён.") + raise ValidationError("Срок подачи проектов в программу завершен.") if program.is_manager(user): return diff --git a/partner_programs/services/project_fields.py b/partner_programs/services/project_fields.py index 7e401b88..4eb7f1c0 100644 --- a/partner_programs/services/project_fields.py +++ b/partner_programs/services/project_fields.py @@ -127,7 +127,7 @@ def submit_program_project(link_id, user): if link.submitted: raise ValidationError({"detail": "Проект уже был сдан на проверку."}) if not link.partner_program.is_project_submission_open(): - raise ValidationError({"detail": "Срок подачи проектов в программу завершён."}) + raise ValidationError({"detail": "Срок подачи проектов в программу завершен."}) try: validate_case_before_submission(link) except DjangoValidationError as error: diff --git a/partner_programs/tests/test_attention_analytics_api.py b/partner_programs/tests/test_attention_analytics_api.py index 78c3cf7c..e458c7d5 100644 --- a/partner_programs/tests/test_attention_analytics_api.py +++ b/partner_programs/tests/test_attention_analytics_api.py @@ -598,7 +598,7 @@ def test_assignments_without_scores_have_no_completed_evaluations_reason(self): self.assignment(self.work()) item = self.get(PROJECTS)["results"][0] self.assertEqual(item["reason"], "no_completed_evaluations") - self.assertEqual(item["reason_label"], "Нет завершённых оценок") + self.assertEqual(item["reason_label"], "Нет завершенных оценок") self.assertEqual(item["assignments_total"], 1) self.assertEqual(item["assignments_completed"], 0) diff --git a/partner_programs/tests/test_program_project_submit.py b/partner_programs/tests/test_program_project_submit.py index a5f153f2..5a8f7251 100644 --- a/partner_programs/tests/test_program_project_submit.py +++ b/partner_programs/tests/test_program_project_submit.py @@ -44,7 +44,7 @@ def test_submit_blocked_after_deadline(self): self.assertEqual(response.status_code, 400) self.assertEqual( response.data.get("detail"), - "Срок подачи проектов в программу завершён.", + "Срок подачи проектов в программу завершен.", ) link.refresh_from_db() self.assertFalse(link.submitted) diff --git a/partner_programs/tests/test_project_apply.py b/partner_programs/tests/test_project_apply.py index ecb307a5..5947da4c 100644 --- a/partner_programs/tests/test_project_apply.py +++ b/partner_programs/tests/test_project_apply.py @@ -208,5 +208,5 @@ def test_apply_project_is_blocked_after_submission_deadline(self): self.assertEqual(response.status_code, 400) self.assertEqual( response.data[0], - "Срок подачи проектов в программу завершён.", + "Срок подачи проектов в программу завершен.", ) diff --git a/project_rates/notifications.py b/project_rates/notifications.py index b9c6fdbe..ca5daa2e 100644 --- a/project_rates/notifications.py +++ b/project_rates/notifications.py @@ -60,7 +60,7 @@ def get(self, request, notification_id=None): { "id": row.pk, "text": ( - f"Эксперт {row.expert_name} ({row.expert_email}) вошёл на платформу " + f"Эксперт {row.expert_name} ({row.expert_email}) вошел на платформу " f"и принял приглашение в программу «{row.invitation.partner_program.name}»." ), "program_id": row.invitation.partner_program_id, diff --git a/project_rates/services.py b/project_rates/services.py index baf42ef6..d4ffef3a 100644 --- a/project_rates/services.py +++ b/project_rates/services.py @@ -122,7 +122,7 @@ def submit_project_scores(*, user, project_id: int, data) -> None: ) and expected_revision != revision(program): raise CriteriaConflict( "criteria_changed", - "Критерии изменились. Обновите форму оценки; введённые ответы сохранены в форме.", + "Критерии изменились. Обновите форму оценки; введенные ответы сохранены в форме.", ) rating_data, criteria_ids, program = _prepare_project_score_data( user=user, @@ -189,7 +189,7 @@ def _prepare_project_score_data( program.datetime_evaluation_ends and timezone.now() > program.datetime_evaluation_ends ): - raise ValidationError("Срок оценивания завершён.") + raise ValidationError("Срок оценивания завершен.") required = set(program.criterias.exclude(type="str").values_list("pk", flat=True)) if not required.issubset(criteria_ids): diff --git a/project_rates/validators.py b/project_rates/validators.py index 11470423..4841efdc 100644 --- a/project_rates/validators.py +++ b/project_rates/validators.py @@ -27,12 +27,12 @@ def _validate_data_type(criteria_type: str, value: str): ): raise ValueError("Требуется конечное число подходящего типа.") except ValueError: - raise ValueError("Введённое значение не соответствует формату!") + raise ValueError("Введенное значение не соответствует формату!") except TypeError: raise TypeError("Вы не ввели никакие данные!") elif (criteria_type == "bool") and (value not in ["True", "False"]): - raise TypeError("Введённое значение не соответствует формату!") + raise TypeError("Введенное значение не соответствует формату!") @staticmethod def _validate_numeric_limits( diff --git a/project_rates/workspace.py b/project_rates/workspace.py index de6013fc..81f26586 100644 --- a/project_rates/workspace.py +++ b/project_rates/workspace.py @@ -446,7 +446,7 @@ def save_project_experts(program_id, user, data): if assignment.has_scores(): raise WorkspaceConflict( "scores_protected", - "Нельзя снять эксперта с сохранённой оценкой.", + "Нельзя снять эксперта с сохраненной оценкой.", ) removals.append(assignment.pk) check_assignment_limit( @@ -700,7 +700,7 @@ def save_assignments(program_id, user, expert_id, data): ) if protected - wanted: raise WorkspaceConflict( - "scores_protected", "Нельзя снять назначение с сохранёнными оценками." + "scores_protected", "Нельзя снять назначение с сохраненными оценками." ) check_assignment_limit(program, [(pid, expert.pk) for pid in wanted - existing]) program.project_expert_assignments.filter( diff --git a/projects/admin.py b/projects/admin.py index c1fb7c02..afefa98f 100644 --- a/projects/admin.py +++ b/projects/admin.py @@ -28,8 +28,8 @@ class ProjectCompanyInline(admin.TabularInline): extra = 1 autocomplete_fields = ("company", "decision_maker") fields = ("company", "contribution", "decision_maker") - verbose_name = "Партнёр проекта" - verbose_name_plural = "Партнёры проекта" + verbose_name = "Партнер проекта" + verbose_name_plural = "Партнеры проекта" class ResourceInline(admin.StackedInline): diff --git a/projects/migrations/0035_interface_copy.py b/projects/migrations/0035_interface_copy.py new file mode 100644 index 00000000..6f2cfa7d --- /dev/null +++ b/projects/migrations/0035_interface_copy.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.11 on 2026-10-20 09:27 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('projects', '0034_default_project_name'), + ] + + operations = [ + migrations.AlterField( + model_name='resource', + name='partner_company', + field=models.ForeignKey(blank=True, help_text='Если не указано — ресурс в поиске партнера.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='resources', to='projects.company'), + ), + ] diff --git a/projects/models.py b/projects/models.py index 3ac788da..d0ac1a3e 100644 --- a/projects/models.py +++ b/projects/models.py @@ -519,7 +519,7 @@ class ResourceType(models.TextChoices): null=True, blank=True, related_name="resources", - help_text="Если не указано — ресурс в поиске партнёра.", + help_text="Если не указано — ресурс в поиске партнера.", ) class Meta: @@ -536,7 +536,7 @@ def partner_display(self): return ( self.partner_company.name if self.partner_company - else "в поиске партнёра для данного ресурса" + else "в поиске партнера для данного ресурса" ) def clean(self): @@ -552,8 +552,8 @@ def clean(self): raise ValidationError( { "partner_company": ( - "Эта компания не является партнёром данного проекта. " - "Сначала добавьте её в партнёры проекта." + "Эта компания не является партнером данного проекта. " + "Сначала добавьте ее в партнеры проекта." ) } ) diff --git a/projects/permissions.py b/projects/permissions.py index 7bb0890d..47197688 100644 --- a/projects/permissions.py +++ b/projects/permissions.py @@ -265,7 +265,7 @@ def has_object_permission(self, request, view, obj): class CanBindProjectToProgram(BasePermission): - message = "Привязать проект к программе может только её участник (или менеджер)." + message = "Привязать проект к программе может только ее участник (или менеджер)." def has_permission(self, request, view): program_id = (request.data or {}).get("partner_program_id") @@ -280,12 +280,12 @@ def has_permission(self, request, view): submission_deadline = program.get_project_submission_deadline() if submission_deadline and submission_deadline < timezone.now(): raise ValidationError( - {"partner_program_id": "Срок подачи проектов в программу завершён."} + {"partner_program_id": "Срок подачи проектов в программу завершен."} ) if program.datetime_finished < timezone.now(): raise ValidationError( - {"partner_program_id": "Нельзя выбрать завершённую программу."} + {"partner_program_id": "Нельзя выбрать завершенную программу."} ) if program.is_manager(request.user): diff --git a/projects/serializers.py b/projects/serializers.py index 27a1c84b..e63a4b69 100644 --- a/projects/serializers.py +++ b/projects/serializers.py @@ -211,7 +211,7 @@ def validate(self, attrs): if not exists: raise serializers.ValidationError( { - "partner_company": "Эта компания не является партнёром данного проекта." + "partner_company": "Эта компания не является партнером данного проекта." } ) return attrs @@ -523,7 +523,7 @@ def validate(self, data): partner_program = PartnerProgram.objects.get(pk=partner_program_id) except PartnerProgram.DoesNotExist: raise serializers.ValidationError( - "Партнёрская программа с указанным ID не найдена." + "Партнерская программа с указанным ID не найдена." ) exists = PartnerProgramProject.objects.filter( @@ -532,7 +532,7 @@ def validate(self, data): if exists: raise serializers.ValidationError( - f"Проект с именем '{project.name}' уже привязан к партнёрской программе '{partner_program.name}'." + f"Проект с именем '{project.name}' уже привязан к партнерской программе '{partner_program.name}'." ) return data diff --git a/projects/team_service.py b/projects/team_service.py index c146b7cb..d0d5786f 100644 --- a/projects/team_service.py +++ b/projects/team_service.py @@ -328,7 +328,7 @@ def bind_project_to_program(*, project_id, program_id, actor, program_link_id=No or program.datetime_finished < timezone.now() ): raise TeamError( - "submission_closed", "Срок подачи проектов в программу завершён." + "submission_closed", "Срок подачи проектов в программу завершен." ) if ( not program.is_manager(actor) @@ -345,7 +345,7 @@ def bind_project_to_program(*, project_id, program_id, actor, program_link_id=No if current_link.team_invites.exists(): raise TeamError( "program_context_in_use", - "Связь программы используется приглашениями; её нельзя удалить.", + "Связь программы используется приглашениями; ее нельзя удалить.", ) PartnerProgramUserProfile.objects.filter( project=project, partner_program_id=current_link.partner_program_id diff --git a/projects/views.py b/projects/views.py index b354ec8d..77119dc5 100644 --- a/projects/views.py +++ b/projects/views.py @@ -559,7 +559,7 @@ def _copy_collaborators(original_project: Project, new_project: Project) -> None @swagger_auto_schema( request_body=ProjectDuplicateRequestSerializer, responses={201: ProjectDuplicateRequestSerializer(), 400: "Validation error"}, - operation_description="Дублирует проект и привязывает его к указанной партнёрской программе", + operation_description="Дублирует проект и привязывает его к указанной партнерской программе", ) def post(self, request): serializer = ProjectDuplicateRequestSerializer( @@ -651,14 +651,14 @@ def create(self, request, *args, **kwargs): def update(self, request, *args, **kwargs): if isinstance(request.data, list): return Response( - {"detail": "Обновление выполняется для одной цели по её ID."}, status=400 + {"detail": "Обновление выполняется для одной цели по ее ID."}, status=400 ) return super().update(request, *args, **kwargs) def partial_update(self, request, *args, **kwargs): if isinstance(request.data, list): return Response( - {"detail": "Частичное обновление выполняется для одной цели по её ID."}, + {"detail": "Частичное обновление выполняется для одной цели по ее ID."}, status=400, ) return super().partial_update(request, *args, **kwargs) @@ -710,7 +710,7 @@ class ProjectCompanyUpsertView(APIView): request_body=ProjectCompanyUpsertSerializer, responses={201: ProjectCompanySerializer}, operation_summary="Создать или привязать компанию к проекту (upsert)", - operation_description="Если компания с таким ИНН уже есть — создаёт/обновляет связь. Если нет — создаёт.", + operation_description="Если компания с таким ИНН уже есть — создает/обновляет связь. Если нет — создает.", tags=["Projects • Companies"], ) def post(self, request, project_id: int): @@ -742,7 +742,7 @@ class ProjectCompaniesListView(ListAPIView): permission_classes = (ProjectVisibilityPermission, IsProjectLeaderOrReadOnly) @swagger_auto_schema( - operation_summary="Список партнёров проекта", + operation_summary="Список партнеров проекта", operation_description="Возвращает связи ProjectCompany с вложенными данными компании для указанного проекта.", responses={200: ProjectCompanySerializer(many=True)}, tags=["Projects • Companies"], @@ -822,7 +822,7 @@ def _get_link_or_404(self, project_id: int, company_id: int): operation_summary="Обновить вклад и/или ответственного компании в проекте", operation_description=( "Позволяет изменить поля связи `ProjectCompany`, такие как `contribution` " - "и `decision_maker`. Компания остаётся без изменений." + "и `decision_maker`. Компания остается без изменений." ), manual_parameters=[project_id_param, company_id_param], request_body=ProjectCompanyUpdateSerializer, @@ -866,7 +866,7 @@ def patch(self, request, project_id: int, company_id: int): operation_summary="Удалить связь проекта с компанией", operation_description=( "Удаляет запись `ProjectCompany`, связывающую проект и компанию. " - "Сама компания при этом остаётся в базе данных." + "Сама компания при этом остается в базе данных." ), manual_parameters=[project_id_param, company_id_param], responses={ diff --git a/templates/email/expert_invitation.html b/templates/email/expert_invitation.html index 59138546..64d75d60 100644 --- a/templates/email/expert_invitation.html +++ b/templates/email/expert_invitation.html @@ -5,9 +5,9 @@
PROCOLLAB

Вас пригласили стать экспертом

-

Здравствуйте!

Программа «{{ program_name }}» ждёт вас в качестве эксперта.

+

Здравствуйте!

Программа «{{ program_name }}» ждет вас в качестве эксперта.

Приглашение для {{ email }}. После принятия организатор сможет назначить вам проекты на оценку.

Принять приглашение

-

Если аккаунта ещё нет, вы сможете создать его по этой ссылке.

+

Если аккаунта еще нет, вы сможете создать его по этой ссылке.

Ссылка действует до {{ expires_at|date:"d.m.Y H:i" }}. Если вы не ожидали приглашение, просто проигнорируйте письмо.

diff --git a/templates/email/expert_invitation.txt b/templates/email/expert_invitation.txt index 6cedc868..7f2a0c01 100644 --- a/templates/email/expert_invitation.txt +++ b/templates/email/expert_invitation.txt @@ -5,6 +5,6 @@ Принять приглашение: {{ invitation_url }} -Если аккаунта ещё нет, вы сможете создать его по этой ссылке. +Если аккаунта еще нет, вы сможете создать его по этой ссылке. Ссылка действует до {{ expires_at|date:"d.m.Y H:i" }}. Если вы не ожидали приглашение, просто проигнорируйте письмо. diff --git a/templates/email/expert_joined.html b/templates/email/expert_joined.html index aeff973b..b30a9303 100644 --- a/templates/email/expert_joined.html +++ b/templates/email/expert_joined.html @@ -2,7 +2,7 @@ Эксперт принял приглашение

Эксперт принял приглашение

-

{{ expert_name }} ({{ expert_email }}) вошёл на платформу и присоединился к программе «{{ program_name }}» в роли эксперта.

+

{{ expert_name }} ({{ expert_email }}) вошел на платформу и присоединился к программе «{{ program_name }}» в роли эксперта.

Теперь ему можно назначать проекты на оценку.

Открыть список экспертов

diff --git a/templates/email/expert_joined.txt b/templates/email/expert_joined.txt index 12974af9..6e550560 100644 --- a/templates/email/expert_joined.txt +++ b/templates/email/expert_joined.txt @@ -1,4 +1,4 @@ -Эксперт вошёл на платформу и принял приглашение +Эксперт вошел на платформу и принял приглашение {{ expert_name }} ({{ expert_email }}) присоединился к программе «{{ program_name }}» в роли эксперта. Теперь ему можно назначать проекты на оценку. diff --git a/templates/email/organizer_invitation.html b/templates/email/organizer_invitation.html index 0ecab906..2744e769 100644 --- a/templates/email/organizer_invitation.html +++ b/templates/email/organizer_invitation.html @@ -8,6 +8,6 @@

Вас пригласили стат

Программа «{{ program_name }}» на PROCOLLAB.

Приглашение для {{ email }}. После принятия вы сможете управлять этой программой и приглашать коллег.

Принять приглашение

-

Если аккаунта ещё нет, вы сможете зарегистрироваться по этой ссылке.

+

Если аккаунта еще нет, вы сможете зарегистрироваться по этой ссылке.

Ссылка действует до {{ expires_at|date:"d.m.Y H:i" }}. Если вы не ожидали приглашение, просто проигнорируйте письмо.

diff --git a/templates/email/organizer_invitation.txt b/templates/email/organizer_invitation.txt index f603779e..9473d9de 100644 --- a/templates/email/organizer_invitation.txt +++ b/templates/email/organizer_invitation.txt @@ -5,6 +5,6 @@ Принять приглашение: {{ invitation_url }} -Если аккаунта ещё нет, вы сможете зарегистрироваться по этой ссылке. +Если аккаунта еще нет, вы сможете зарегистрироваться по этой ссылке. Ссылка действует до {{ expires_at|date:"d.m.Y H:i" }}. Если вы не ожидали приглашение, просто проигнорируйте письмо. diff --git a/users/admin.py b/users/admin.py index fd4cf711..d388242e 100644 --- a/users/admin.py +++ b/users/admin.py @@ -68,7 +68,7 @@ class UserLanguagesInline(admin.TabularInline): verbose_name_plural = "Знание языков" -@admin.action(description="Сделать выбранных пользователей подтверждёнными") +@admin.action(description="Сделать выбранных пользователей подтвержденными") def make_active(modeladmin, request, queryset): queryset.update(is_active=True, verification_date=now().date()) diff --git a/vacancy/mapping.py b/vacancy/mapping.py index e92d0864..1f58edca 100644 --- a/vacancy/mapping.py +++ b/vacancy/mapping.py @@ -56,10 +56,10 @@ class ProjectRatedParams(BaseEmailCeleryParams): message_type_to_title: dict[Annotated[str, EmailTextTypes], str] = { MessageTypeEnum.ACCEPTED.value: "На ваш отклик ответили", MessageTypeEnum.RESPONDED.value: "На вашу вакансию откликнулись", - MessageTypeEnum.OUTDATED.value: "У вашей вакансии истёк срок годности", + MessageTypeEnum.OUTDATED.value: "У вашей вакансии истек срок годности", MessageTypeEnum.REJECTED.value: "На ваш отклик ответили отказом", MessageTypeEnum.REGISTERED_PROGRAM_USER.value: "Вы зарегистрировались на программу", - MessageTypeEnum.PROJECT_RATED.value: "Ваш проект был оценён", + MessageTypeEnum.PROJECT_RATED.value: "Ваш проект был оценен", } @@ -94,7 +94,7 @@ def create_text_for_email(data: EmailParamsType) -> str: return f"На вакансию {data['vacancy_role']} для проекта '{data['project_name']}' оставили отклик." case MessageTypeEnum.OUTDATED.value: return f""" - У вакансии {data["vacancy_role"]} для проекта '{data["project_name"]}' истёк срок годности.\n + У вакансии {data["vacancy_role"]} для проекта '{data["project_name"]}' истек срок годности.\n Она более не будет показываться в поиске. """ case MessageTypeEnum.REJECTED.value: @@ -108,6 +108,6 @@ def create_text_for_email(data: EmailParamsType) -> str: """ case MessageTypeEnum.PROJECT_RATED.value: return f""" - Ваш проект '{data["project_name"]}' был оценён экспертом по программе '{data["program_name"]}'! + Ваш проект '{data["project_name"]}' был оценен экспертом по программе '{data["program_name"]}'! Скоро будут результаты. """