@@ -1,12 +1,17 @@ from authentication.exceptions.business_host_exceptions.base_already import ( BaseAlready, ) +from django.utils.translation import gettext_lazy as _ class AlreadyHasPlan(BaseAlready): def msg(self): return dict( - message='user has a paid plan', + message=_( + 'You already have an active tariff plan. You must request a ' + 'cancellation of your current tariff plan, after which ' + 'you will be able to create a Corporate Account.' + ), user_id=self.user.uid, plan_id=self.user.payment_plan.plan.uid, ) @@ -8,9 +8,6 @@ __all__ = ('BaseAlready',) class InvalidPassword(Exception): ... -class InvalidUsername(Exception): ... - - class InvalidToken(Exception): def __str__(self) -> str: return 'Invalid token received, please try relog' @@ -0,0 +1,27 @@ +# Generated by Django 5.0.11 on 2025-11-06 14:11 + +import django.db.models.functions.text +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ('authentication', '0024_alter_customusermodel_email'), + ] + + operations = [ + migrations.RemoveIndex( + model_name='customusermodel', + name='idx_users_email_upper', + ), + migrations.RemoveField( + model_name='customusermodel', + name='username', + ), + migrations.AddIndex( + model_name='customusermodel', + index=models.Index(django.db.models.functions.text.Upper('email'), name='idx_users_email_upper'), + ), + ] @@ -30,7 +30,6 @@ if TYPE_CHECKING: class CustomUserModelManager(BaseUserManager): def create_user( self, - username: str, email: str, password: Optional[str] = None, **kwargs, @@ -44,15 +43,15 @@ class CustomUserModelManager(BaseUserManager): email = email.lower() - user = self.model(username=username, email=email, **kwargs) + user = self.model(email=email, **kwargs) user.set_password(password) user.save(using=self._db) return user - def create_superuser(self, username, email, password): - user = self.create_user(username, email, password=password) + def create_superuser(self, email, password): + user = self.create_user(email, password=password) user.is_staff = True user.is_superuser = True @@ -69,8 +68,7 @@ class CustomUserModelManager(BaseUserManager): class CustomUserModel(AbstractBaseUser, PermissionsMixin, BaseModel): - USERNAME_FIELD = 'username' - REQUIRED_FIELDS = ['email'] + USERNAME_FIELD = 'email' FIRST_NAME_PLACEHOLDERS = [ 'Любопытный', @@ -137,7 +135,6 @@ class CustomUserModel(AbstractBaseUser, PermissionsMixin, BaseModel): default=random_last_name, verbose_name=_('Last name'), ) - username = models.CharField(max_length=100, unique=True, verbose_name=_('Username')) email = models.EmailField(max_length=100, unique=True, verbose_name=_('Email')) active = models.BooleanField(default=True, verbose_name=_('Is active')) @@ -235,7 +232,7 @@ class CustomUserModel(AbstractBaseUser, PermissionsMixin, BaseModel): class Meta: ordering = ('-created_at',) - indexes = (Index(Upper('username'), name='idx_users_email_upper'),) + indexes = (Index(Upper('email'), name='idx_users_email_upper'),) verbose_name = _('User') verbose_name_plural = _('User') @@ -50,7 +50,6 @@ from authentication.services.business_account_service import ( from authentication.services.email_service import EmailService from authentication.utils import generate_token from ml_model.models import NeuronModel -from ml_model.utils import random_with_N_digits from payments.models.payment_plan import PaymentPlan from payments.selectors.payment_plan_selector import PaymentPlanSelector from payments.services.payment_plan_service import PaymentPlanService @@ -66,9 +65,8 @@ class BusinessHostService: token_limit: Decimal | None = None, account_privileges: Tuple[str, Any] | None = None, ) -> BusinessAccountService: - username = f'{self.user.host_account.company_name}_{random_with_N_digits(6)}' password = generate_token(15) - user = CustomUserModel.objects.create_user(username=username, email=email, password=password) + user = CustomUserModel.objects.create_user(email=email, password=password) user.save() PaymentPlanService(user).subscribe_user_to_plan(PaymentPlan.objects.get(price=0, is_corporate=False)) @@ -79,7 +79,7 @@ class EmailService: def send_error_email(self, error: ErrorReport): message = f""" - Пользователь {error.author.username} сообщил об ошибке: + Пользователь {error.author.email} сообщил об ошибке: {error.report_text} Скриншоты ошибки (если пользователь их приложил) - во вложении """ @@ -100,7 +100,7 @@ class EmailService: def send_copr_purchase_email(self, host: BusinessUserHost): message = f""" - Пользователь {host.user.username} зарегистрировал корпоративный аккаунт: + Пользователь {host.user.email} зарегистрировал корпоративный аккаунт: Компания: {host.company_name} Сектор: {host.company_sector} @@ -169,7 +169,7 @@ class EmailService: Если ссылка не открывается по кнопке, попробуйте вставить ссылку в адресную строку: {}?token={} """, - account.user.username, + account.user.email, self.user.host_account.company_name, settings.INVITATION_RESPONSE_URL, token.key, @@ -9,18 +9,18 @@ from django.contrib.auth.hashers import check_password from authentication.exceptions import ( InvalidPassword, InvalidToken, - InvalidUsername, ) +from authentication.exceptions.user import WrongEmail from authentication.models.user import CustomUserModel class TokenService: @classmethod - def issue(cls, *, username: str, password: str) -> dict[str, str]: + def issue(cls, *, email: str, password: str) -> dict[str, str]: try: - user = CustomUserModel.objects.get(username=username) + user = CustomUserModel.objects.get(email=email) except CustomUserModel.DoesNotExist: - raise InvalidUsername + raise WrongEmail if not check_password(password=password, encoded=user.password): raise InvalidPassword @@ -59,15 +59,15 @@ class UserService: serializer = NewUserSerializer(data=request.data) serializer.is_valid(raise_exception=True) user_data = serializer.validated_data - username = user_data["email"] - if user := CustomUserModel.objects.filter(email=username).first(): + email = user_data['email'] + if user := CustomUserModel.objects.filter(email=email).first(): if not user.is_deleted: raise UserAlreadyExists user.is_deleted = False user.set_password(user_data["password"]) else: user = CustomUserModel.objects.create_user( - username=username, email=username, password=user_data["password"] + email=email, password=user_data['password'] ) UTMService(user).create_utm( user_data.get("utm_source"), @@ -77,11 +77,11 @@ class UserService: user_data.get("utm_content"), ) EmailService(user).send_reg_conf_email() - if referer_username := user_data.get("referer"): + if referer_email := user_data.get('referer'): try: referer = ( - CustomUserModel.objects.filter(email=referer_username) - .prefetch_related("user_referral_account") + CustomUserModel.objects.filter(email=referer_email) + .prefetch_related('user_referral_account') .get() ) ReferralAccountService.create_invite( @@ -123,8 +123,8 @@ class UserService: raise WrongEmail user = authenticate( - username=probable_user.username, - password=serializer.validated_data["password"], + username=probable_user.email, + password=serializer.validated_data['password'], ) if user is None: @@ -237,7 +237,7 @@ class UserService: serializer = UpdateUserDataSerializer(data=request.data) serializer.is_valid(raise_exception=True) - for field in ("username", "email", "first_name", "last_name"): + for field in ('email', 'first_name', 'last_name'): setattr( self.user, field, @@ -248,8 +248,8 @@ class UserService: if new_profile_picture is not None: new_picture_name = MinIOService().put_object( new_profile_picture, - f"{self.user.username}.png", - "air-profiles", + f'{self.user.email}.png', + 'air-profiles', ) self.user.profile_picture_name = new_picture_name @@ -260,8 +260,8 @@ class UserService: serializer.is_valid(raise_exception=True) user = authenticate( - username=self.user.username, - password=serializer.validated_data["current_password"], + username=self.user.email, + password=serializer.validated_data['current_password'], ) if user is None: raise Exception(_("Current password is wrong")) @@ -279,10 +279,8 @@ class UserService: serializer = UpdateProfilePictureSerializer(data=request.data) serializer.is_valid(raise_exception=True) - img = serializer.validated_data["new_picture"] - img_name = MinIOService().put_object( - img, f"{self.user.username}.png", "air-profiles" - ) + img = serializer.validated_data['new_picture'] + img_name = MinIOService().put_object(img, f'{self.user.email}.png', 'air-profiles') self.user.profile_picture_name = img_name self.user.save() @@ -344,8 +342,5 @@ def detect_email(backend, response, details, **kwargs): and not response.get("default_email") and (login := response.get("login")) ): - details["email"] = ( - f"{login.split('@')[0]}@yandex.ru" if "@" not in login else login - ) - details["username"] = response["login"] - return kwargs | {"backend": backend} | {"response": response} | {"details": details} + details['email'] = f'{login.split("@")[0]}@yandex.ru' if '@' not in login else login + return kwargs | {'backend': backend} | {'response': response} | {'details': details} @@ -117,12 +117,16 @@ class HasInviteListFilter(admin.SimpleListFilter): @admin.register(CustomUserModel) class CustomUserModelAdmin(UserAdmin, ExportActionModelAdmin): list_display = ['email', '_balance', 'created_at'] - search_fields = ['email', 'created_at', 'utm__utm_source'] + date_hierarchy = 'created_at' + ordering = ('email',) + search_fields = ['email', 'utm__utm_source'] + + search_help_text = 'Можно искать по email пользователя, UTM-источнику' + readonly_fields = ['uid'] resource_classes = (DefaultUserResource, ReferralUserResource) fields = [ 'uid', - 'username', 'email', 'password', 'first_name', @@ -145,11 +149,7 @@ class CustomUserModelAdmin(UserAdmin, ExportActionModelAdmin): HasInviteListFilter, AdditionalListFilter, ] - verbose_name = 'Пользователь' - verbose_name_plural = 'Пользователи' - actions = [ - 'download_users', - ] + actions = ['download_users'] def get_queryset(self, request): qs = super().get_queryset(request) @@ -27,7 +27,6 @@ PATH_PREFETCH_MAP = { 'uid', 'first_name', 'last_name', - 'username', 'created_at', 'email', 'active', @@ -19,7 +19,6 @@ class UserSchema(Schema): uid: UUID4 first_name: str last_name: str - username: str created_at: datetime email: str is_active: bool @@ -93,7 +93,6 @@ class RequestPassChangeSerializer(serializers.Serializer): class UpdateUserDataSerializer(serializers.Serializer): - username = serializers.CharField(required=False) email = serializers.EmailField(required=False) profile_picture = serializers.ImageField(required=False, write_only=True) first_name = serializers.CharField(required=False) @@ -101,7 +100,6 @@ class UpdateUserDataSerializer(serializers.Serializer): class UserDataSerializer(serializers.Serializer): - username = serializers.CharField() email = serializers.EmailField() is_superuser = serializers.BooleanField() account_type = serializers.SerializerMethodField() @@ -129,7 +127,6 @@ class UserDetailSerializer(serializers.Serializer): uid = serializers.UUIDField() first_name = serializers.CharField() last_name = serializers.CharField() - username = serializers.CharField() created_at = serializers.DateTimeField() email = serializers.CharField() is_active = serializers.BooleanField() @@ -459,6 +459,7 @@ if (SENTRY_URL := env.str('SENTRY_URL', '')) and RELEASE and ENVIRONMENT: cache_spans=False, ), ], + ignore_errors=['InsufficientBalance'] ) CACHEOPS_REDIS = env.str('CACHEOPS_REDIS', CACHES['default']['LOCATION']) @@ -13,7 +13,6 @@ from ninja import NinjaAPI from authentication.exceptions import ( InvalidPassword, InvalidToken, - InvalidUsername, ) from backend.public import urlpatterns as public_urlpatterns @@ -47,11 +46,6 @@ def invalid_password_error_handler(request, exc: InvalidPassword): return api.create_response(request, {'message': _('Wrong password')}, status=401) -@api.exception_handler(InvalidUsername) -def invalid_username_error_handler(request, exc: InvalidUsername): - return api.create_response(request, {'message': _('Wrong username')}, status=401) - - def healthz_status(request): return JsonResponse({'status': 'ok'}, status=200) @@ -2,14 +2,13 @@ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. -# +# #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2025-07-08 16:09+0300\n" -"POT-Creation-Date: 2025-05-10 12:12+0300\n" +"POT-Creation-Date: 2025-11-06 17:57+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -21,60 +20,26 @@ msgstr "" "n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || " "(n%100>=11 && n%100<=14)? 2 : 3);\n" -#: achievements/admin.py:11 achievements/models.py:19 ml_model/models.py:32 -#: stories/models.py:18 -msgid "Icon" -msgstr "Миниатюра" - -#: achievements/admin.py:22 achievements/models.py:29 achievements/models.py:37 -msgid "Achievement" -msgstr "Достижение" - -#: achievements/apps.py:8 achievements/models.py:30 -msgid "Achievements" -msgstr "Достижения" - -#: achievements/models.py:14 ml_model/models.py:25 ml_model/models.py:52 -#: ml_model/models.py:231 ml_model/models.py:357 -msgid "Slug" -msgstr "Ярлык" - -#: achievements/models.py:16 ml_model/models.py:51 ml_model/models.py:146 -#: ml_model/models.py:230 ml_model/models.py:355 payments/models/payment.py:52 -msgid "Description" -msgstr "Описание" - -#: achievements/models.py:43 authentication/models/business_host.py:22 -#: authentication/models/email_token.py:12 authentication/models/user.py:246 -#: authentication/models/user.py:247 authentication/models/user_telegram.py:22 -#: authentication/models/user_vk.py:12 payments/models/invoice.py:15 -#: payments/models/payment.py:26 payments/models/payment_plan.py:61 -msgid "User" -msgstr "Пользователь" - -#: achievements/models.py:46 -msgid "Issued at" -msgstr "Когда выдано" - -#: achievements/models.py:49 -#, python-format -msgid "Achievement %(achievement_title)s пользователя %(username)s" -msgstr "Достижение %(achievement_title)s пользователя %(username)s" - -#: achievements/models.py:55 achievements/models.py:56 -msgid "Issued achievement" -msgstr "Выданное достижение" - #: authentication/exceptions/business_account.py:6 msgid "Business account not found" msgstr "Сотрудник не найден" #: authentication/exceptions/business_host_exceptions/access_denied.py:6 -#: authentication/services/business_account_service.py:103 -#: authentication/services/business_account_service.py:106 +#: authentication/services/business_account_service.py:108 +#: authentication/services/business_account_service.py:111 msgid "You do not have sufficient rights to perform this action" msgstr "У вас недостаточно прав для выполнения этого действия" +#: authentication/exceptions/business_host_exceptions/already_has_plan.py:11 +msgid "" +"You already have an active tariff plan. You must request a cancellation of " +"your current tariff plan, after which you will be able to create a Corporate " +"Account." +msgstr "" +"У вас уже активен тарифный план. Необходимо запросить аннулирование текущего " +"тарифного плана, после чего у вас появится возможность создать Корпоративный " +"Аккаунт" + #: authentication/exceptions/business_host_exceptions/not_allowed_ip.py:6 msgid "Current IP not allowed in this context" msgstr "Текущий IP не разрешен в данном контексте" @@ -87,6 +52,10 @@ msgstr "Шаблон письма не найден" msgid "There was an unknown error while sending an email" msgstr "При отправке письма произошла неизвестная ошибка" +#: authentication/exceptions/email_exceptions/token_not_found.py:6 +msgid "Email token not found. Please contact support" +msgstr "E-mail токен не найден. Пожалуйста, свяжитесь со службой поддержки" + #: authentication/exceptions/email_token.py:6 #, fuzzy #| msgid "No email token provided" @@ -97,7 +66,7 @@ msgstr "Токен не получен" msgid "Wrong email" msgstr "Неверный email" -#: authentication/exceptions/user.py:11 backend/urls.py:41 +#: authentication/exceptions/user.py:11 backend/urls.py:46 msgid "Wrong password" msgstr "Неверный пароль" @@ -165,10 +134,9 @@ msgstr "Дочерний Бизнес Аккаунт" msgid "Child Business Accounts" msgstr "Дочерние Бизнес Аккаунты" -#: authentication/models/business_group.py:8 ml_model/models.py:24 -#: ml_model/models.py:145 ml_model/models.py:348 -#: payments/models/payment_plan.py:27 stories/models.py:12 stories/models.py:35 -#: tools/chats/models.py:9 +#: authentication/models/business_group.py:8 ml_model/models.py:26 +#: ml_model/models.py:185 ml_model/models.py:429 +#: payments/models/payment_plan.py:27 tools/chats/models.py:9 msgid "Title" msgstr "Название" @@ -180,14 +148,20 @@ msgstr "Бизнес Группа" msgid "Business Groups" msgstr "Бизнес Группы" +#: authentication/models/business_host.py:22 +#: authentication/models/email_token.py:13 authentication/models/user.py:236 +#: authentication/models/user.py:237 authentication/models/user_telegram.py:22 +#: authentication/models/user_vk.py:12 payments/admin.py:35 +#: payments/admin.py:89 payments/models/invoice.py:15 +#: payments/models/payment.py:26 payments/models/payment_plan.py:61 +msgid "User" +msgstr "Пользователь" + #: authentication/models/business_host.py:31 msgid "Affiliated by" msgstr "Кем привлечена" -#: authentication/models/business_host.py:36 authentication/models/user.py:152 -#: authentication/models/whitelist.py:16 ml_model/models.py:167 -#: payments/models/promocode.py:85 -#: authentication/models/business_host.py:35 authentication/models/user.py:153 +#: authentication/models/business_host.py:36 authentication/models/user.py:140 #: authentication/models/whitelist.py:16 payments/models/promocode.py:85 msgid "Is active" msgstr "Является активной" @@ -224,10 +198,8 @@ msgstr "ИНН" msgid "PSRN" msgstr "ОГРН" -#: authentication/models/business_host.py:69 ml_model/models.py:180 -#: tools/public_api/models.py:30 -#: authentication/models/business_host.py:68 ml_model/models.py:50 -#: ml_model/models.py:228 tools/public_api/models.py:30 +#: authentication/models/business_host.py:69 ml_model/models.py:46 +#: ml_model/models.py:77 ml_model/models.py:288 tools/public_api/models.py:30 msgid "Name" msgstr "Наименование" @@ -331,56 +303,56 @@ msgstr "Админ" msgid "Security" msgstr "Безопасность" -#: authentication/models/email_token.py:15 ml_model/models.py:271 -#: authentication/models/email_token.py:15 ml_model/models.py:147 +#: authentication/models/email_token.py:16 ml_model/models.py:187 msgid "Key" msgstr "Ключ" -#: authentication/models/email_token.py:18 +#: authentication/models/email_token.py:22 msgid "Email Token" msgstr "Email Токен" -#: authentication/models/email_token.py:19 +#: authentication/models/email_token.py:23 msgid "Email Tokens" msgstr "Email Токены" -#: authentication/models/user.py:126 authentication/models/user_telegram.py:9 +#: authentication/models/user.py:129 authentication/models/user_telegram.py:9 msgid "First name" msgstr "Имя" -#: authentication/models/user.py:133 authentication/models/user_telegram.py:10 +#: authentication/models/user.py:136 authentication/models/user_telegram.py:10 msgid "Last name" msgstr "Фамилия" -#: authentication/models/user.py:140 authentication/models/user_telegram.py:11 -msgid "Username" -msgstr "Имя пользователя" - -#: authentication/models/user.py:147 +#: authentication/models/user.py:138 msgid "Email" msgstr "Email" -#: authentication/models/user.py:155 +#: authentication/models/user.py:142 msgid "Is staff" msgstr "Административный" -#: authentication/models/user.py:156 +#: authentication/models/user.py:143 msgid "Is superuser" msgstr "Суперюзер" -#: authentication/models/user.py:157 +#: authentication/models/user.py:144 msgid "Is email confirmed" msgstr "Email подтвержден" -#: authentication/models/user.py:158 +#: authentication/models/user.py:145 msgid "Is subscribed" msgstr "Подписан на уведомления" -#: authentication/models/user.py:164 +#: authentication/models/user.py:151 msgid "Picture name" msgstr "Имя аватара" -#: authentication/models/user.py:173 authentication/models/utm.py:21 +#: authentication/models/user.py:153 tools/chats/models.py:13 +#: tools/public_api/models.py:45 +msgid "Is deleted" +msgstr "Удален" + +#: authentication/models/user.py:161 authentication/models/utm.py:21 msgid "UTM" msgstr "UTM" @@ -392,6 +364,10 @@ msgstr "Телеграм ID" msgid "Is bot" msgstr "Является ботом" +#: authentication/models/user_telegram.py:11 +msgid "Username" +msgstr "Имя пользователя" + #: authentication/models/user_telegram.py:12 msgid "Language" msgstr "Язык" @@ -409,8 +385,8 @@ msgid "Phonenumber" msgstr "Номер телефона" #: authentication/models/user_telegram.py:27 -#: authentication/models/user_vk.py:14 ml_model/models.py:318 -#: payments/models/invoice.py:11 stories/models.py:15 tools/chats/models.py:10 +#: authentication/models/user_vk.py:14 ml_model/models.py:396 +#: payments/models/invoice.py:11 tools/chats/models.py:10 msgid "Created at" msgstr "Когда создан" @@ -467,12 +443,31 @@ msgstr "Вайтлист для отмены политик" msgid "Whitelists to cancel policies" msgstr "Вайтлисты для отмены политик" -#: authentication/selectors/business_host_selector.py:40 -#: authentication/selectors/business_host_selector.py:83 +#: authentication/security.py:41 +#, fuzzy +#| msgid "Hidden" +msgid "Forbidden" +msgstr "Скрытый" + +#: authentication/security.py:54 +msgid "Access token is expired" +msgstr "Срок действия токена доступа истек" + +#: authentication/security.py:67 +msgid "User not found" +msgstr "Пользователь не найден" + +#: authentication/security.py:105 authentication/security.py:136 +msgid "Access token expired or does not exist" +msgstr "Токен доступа просрочен или не существует" + +#: authentication/selectors/business_host_selector.py:38 +#: authentication/selectors/business_host_selector.py:81 msgid "User haven't rights to access host account information" -msgstr "У пользователя недостаточно прав для просмотра информации бизнес-аккаунта" +msgstr "" +"У пользователя недостаточно прав для просмотра информации бизнес-аккаунта" -#: authentication/selectors/business_host_selector.py:58 +#: authentication/selectors/business_host_selector.py:56 msgid "Host user is not registered for this account" msgstr "Пользователь бизнес-аккаунта не зарегистрирован для этого аккаунта" @@ -480,136 +475,123 @@ msgstr "Пользователь бизнес-аккаунта не зареги msgid "No user with this uid found" msgstr "Не найден пользователь с данным ID" -#: authentication/services/business_account_service.py:60 #: authentication/services/business_account_service.py:60 msgid "BusinessAccount for this user doesn't exist" msgstr "Бизнес-аккаунт для данного юзера не найден" -#: authentication/services/business_account_service.py:76 #: authentication/services/business_account_service.py:76 msgid "Invited account can either accept or reject an invitation" msgstr "Приглашенный аккаунт может принять или отклонить приглашение" -#: authentication/services/business_account_service.py:81 -msgid "Account is already confirmed" -msgstr "Аккаунт уже подтвержден" - -#: authentication/services/business_account_service.py:100 +#: authentication/services/business_account_service.py:102 #, fuzzy #| msgid "Passwords do not match" msgid "Passwords don't match" msgstr "Пароли не совпадают" -#: authentication/services/business_account_service.py:111 +#: authentication/services/business_account_service.py:114 msgid "You cannot change the password of an unconfirmed e-mail user." msgstr "Вы не можете изменить пароль неподтвержденного по e-mail пользователя." -#: authentication/services/business_host_service.py:149 +#: authentication/services/business_host_service.py:158 msgid "No user_email is provided" msgstr "" -#: authentication/services/email_service.py:48 #: authentication/services/email_service.py:47 msgid "Error occured when proceed email sending" msgstr "Случилась ошибка во время отправки email" #: authentication/services/email_service.py:125 -#: authentication/services/email_service.py:124 msgid "Regular users cannot send introductory letters" msgstr "Обычные пользователи не могут отсылать письма" -#: authentication/services/email_service.py:162 -#: authentication/services/email_service.py:158 +#: authentication/services/email_service.py:160 msgid "Regular users cannot send invitation letters" msgstr "Обычные пользователи не могут отправлять письма для приглашений" -#: authentication/services/user_services.py:166 -#: authentication/services/user_services.py:162 +#: authentication/services/user_services.py:175 msgid "No user like this in a database" msgstr "Такой пользователь отсутствует" -#: authentication/services/user_services.py:183 -#: authentication/services/user_services.py:179 +#: authentication/services/user_services.py:192 msgid "token is not provided" msgstr "" -#: authentication/services/user_services.py:207 -#: authentication/services/user_services.py:203 +#: authentication/services/user_services.py:216 msgid "No email token provided" msgstr "Токен не получен" -#: authentication/services/user_services.py:211 -#: authentication/services/user_services.py:207 +#: authentication/services/user_services.py:220 msgid "No token like this in a database" msgstr "Не найдено такого токена" -#: authentication/services/user_services.py:217 -#: authentication/services/user_services.py:213 +#: authentication/services/user_services.py:229 msgid "Passwords do not match" msgstr "Пароли не совпадают" -#: authentication/services/user_services.py:255 -#: authentication/services/user_services.py:251 +#: authentication/services/user_services.py:267 msgid "Current password is wrong" msgstr "Текущий пароль неверен" #: authentication/views.py:120 authentication/views.py:229 -#: authentication/views.py:325 authentication/views.py:356 -#: authentication/views.py:119 authentication/views.py:228 -#: authentication/views.py:324 authentication/views.py:354 +#: authentication/views.py:325 authentication/views.py:355 msgid "Server error occured" msgstr "Случилась серверная ошибка" #: authentication/views.py:225 -#: authentication/views.py:224 msgid "Email not found" msgstr "Email не найден" #: authentication/views.py:321 -#: authentication/views.py:320 msgid "Business account has been deleted" msgstr "Сотрудник успешно удален" -#: authentication/views.py:343 +#: authentication/views.py:344 msgid "Business account has been reinvited" msgstr "Повторное приглашение сотруднику успешно отправлено" -#: authentication/views.py:452 +#: authentication/views.py:454 msgid "Could not confirm email, please try again." msgstr "Невозможно подтвердить email, попробуйте позже" -#: backend/urls.py:30 +#: backend/urls.py:36 msgid "Requested object does not exists" msgstr "" -#: backend/urls.py:36 +#: backend/urls.py:41 msgid "Token is invalid" msgstr "" -#: backend/urls.py:46 -msgid "Wrong username" -msgstr "Неверное имя пользователя" - #: core/minio_service.py:35 core/minio_service.py:53 core/minio_service.py:61 #: core/minio_service.py:70 msgid "Unknown bucket destination" msgstr "Неизвестный бакет для загрузки" -#: messages/serializers.py:42 +#: messages/serializers.py:50 #, python-format msgid "The file size cannot exceed %(max_mb_size)d MB" msgstr "Файл не может быть размером больше %(max_mb_size)d мегабайт" -#: ml_model/admin.py:74 ml_model/models.py:263 ml_model/models.py:269 -#: ml_model/models.py:301 ml_model/models.py:323 +#: ml_model/admin.py:134 ml_model/models.py:41 ml_model/models.py:498 +#, fuzzy +#| msgid "Tags" +msgid "Tag" +msgstr "Теги" + +#: ml_model/admin.py:135 ml_model/models.py:42 ml_model/models.py:300 +msgid "Tags" +msgstr "Теги" + +#: ml_model/admin.py:148 ml_model/models.py:339 ml_model/models.py:345 +#: ml_model/models.py:379 ml_model/models.py:401 ml_model/models.py:501 msgid "Inference" msgstr "Инференс" -#: ml_model/admin.py:75 ml_model/models.py:264 ml_model/models.py:377 +#: ml_model/admin.py:149 ml_model/models.py:340 ml_model/models.py:463 msgid "Inferences" msgstr "Инференсы" -#: ml_model/apps.py:8 ml_model/models.py:388 +#: ml_model/apps.py:9 ml_model/models.py:486 msgid "Neuron Models" msgstr "Нейронные Модели" @@ -617,21 +599,49 @@ msgstr "Нейронные Модели" msgid "Inference is currently disabled, retry later." msgstr "Инференс в настоящее время выключен, повторите попытку позже." -#: ml_model/exceptions.py:17 +#: ml_model/exceptions.py:19 +#, python-format +msgid "Parameter %(parameter_name)s not valid, please retry later" +msgstr "Параметр %(parameter_name)s некорректен, повторите попытку позже" + +#: ml_model/exceptions.py:26 +#, fuzzy +#| msgid "Payment Rule" +msgid "Payment Rule not implemented" +msgstr "Платежное правило" + +#: ml_model/exceptions.py:31 ml_model/exceptions.py:40 +msgid "Your request was blocked by our moderation system" +msgstr "Ваш запрос был заблокирован нашей системой модерации" + +#: ml_model/exceptions.py:35 msgid "The model is currently disabled. Please try again later." msgstr "" "Модель в настоящее время неактивна. Пожалуйста, повторите попытку позже." -#: ml_model/exceptions.py:19 +#: ml_model/exceptions.py:51 #, python-format -msgid "Parameter %(parameter_name)s not valid, please retry later" -msgstr "Параметр %(parameter_name)s некорректен, повторите попытку позже" +msgid "" +"Image size %(cw)dx%(ch)d is not supported. Please rotate image to " +"%(rw)dx%(rh)d" +msgstr "" +"Размер изображения %(cw)dx%(ch)d не поддерживается. Пожалуйста, переверните " +"до %(rw)dx%(rh)d" -#: ml_model/exceptions.py:22 -msgid "The model is not responding" -msgstr "Модель не отвечает" +#: ml_model/exceptions.py:55 +#, python-format +msgid "Image size %(cw)sx%(ch)s is not supported. Required size: %(rw)sx%(rh)s" +msgstr "" +"Размер изображения %(cw)sx%(ch)s не поддерживается. Требуемый размер: " +"%(rw)sx%(rh)s" -#: ml_model/exceptions.py:31 +#: ml_model/exceptions.py:60 +#, fuzzy +#| msgid "The payer does not exist" +msgid "Scraper does not exists" +msgstr "Плательщик не существует" + +#: ml_model/exceptions.py:69 #, python-format msgid "" "The attached file format is not supported. Available formats: " @@ -640,429 +650,449 @@ msgstr "" "Формат вложенного файла не поддерживается. Доступные форматы: " "%(available_extensions)s." -#: ml_model/exceptions.py:37 -msgid "The length of the context has been exceeded." -msgstr "Длина контекста превышена." - -#: ml_model/exceptions.py:42 -msgid "Jinja template not found" -msgstr "Jinja-шаблон не найден" - -#: ml_model/exceptions.py:47 -msgid "There was an unknown error while rendering a template" -msgstr "При рендеринге шаблона произошла неизвестная ошибка" +#: ml_model/exceptions.py:75 +msgid "Unknown file format" +msgstr "Неизвестный формат файла" -#: ml_model/models.py:28 ml_model/models.py:80 -msgid "Category" -msgstr "Категория" +#: ml_model/exceptions.py:80 +msgid "The neuron model does not exist" +msgstr "Нейронная модель не существует" -#: ml_model/models.py:29 -msgid "Categories" -msgstr "Категории" +#: ml_model/models.py:27 ml_model/models.py:47 ml_model/models.py:79 +#: ml_model/models.py:291 ml_model/models.py:438 +msgid "Slug" +msgstr "Ярлык" -#: ml_model/models.py:42 +#: ml_model/models.py:31 msgid "Not SVG-pictures not allowed" msgstr "Нельзя использовать не SVG-картинки" -#: ml_model/models.py:39 -#, fuzzy -#| msgid "Tags" -msgid "Tag" -msgstr "Теги" - -#: ml_model/models.py:91 -msgid "Tags" -msgstr "Теги" - -#: ml_model/models.py:156 ml_model/models.py:403 -msgid "Model" -msgstr "Модель" - -#: ml_model/models.py:172 ml_model/models.py:173 -msgid "Settings" -msgstr "Настройки" - -#: ml_model/models.py:176 -#, python-format -msgid "Settings of %(model_title)s" -msgstr "Настройки %(model_title)s" - -#: ml_model/models.py:195 -#, python-format -msgid "%(model_title)s | %(version_name)s" -msgstr "%(model_title)s | %(version_name)s" - -#: ml_model/models.py:201 -msgid "Model Version" -msgstr "Версия Модели" +#: ml_model/models.py:34 +msgid "Icon" +msgstr "Миниатюра" -#: ml_model/models.py:202 -msgid "Model Versions" -msgstr "Версии Модели" +#: ml_model/models.py:57 ml_model/models.py:86 +msgid "Scraper" +msgstr "" -#: ml_model/models.py:211 -msgid "Versions" -msgstr "Версии" +#: ml_model/models.py:60 +msgid "Keyword Arguments" +msgstr "" -#: ml_model/models.py:212 -msgid "Link to versions" -msgstr "Привязка к версиям" +#: ml_model/models.py:67 +#, fuzzy +#| msgid "Runner is missing" +msgid "Scraper is missing" +msgstr "Раннер не найден" -#: ml_model/models.py:221 reports/models/error_report.py:10 +#: ml_model/models.py:72 ml_model/models.py:139 ml_model/models.py:236 +#: ml_model/models.py:424 reports/models/error_report.py:10 msgid "Text" msgstr "Текст" -#: ml_model/models.py:46 +#: ml_model/models.py:73 ml_model/models.py:237 msgid "File" msgstr "Файл" -#: ml_model/models.py:47 +#: ml_model/models.py:74 ml_model/models.py:238 msgid "Embeddings" msgstr "Эмбеддинги" -#: ml_model/models.py:49 ml_model/models.py:227 +#: ml_model/models.py:76 ml_model/models.py:287 msgid "ID" msgstr "ID" -#: ml_model/models.py:63 +#: ml_model/models.py:78 ml_model/models.py:186 ml_model/models.py:290 +#: ml_model/models.py:436 payments/models/payment.py:52 +msgid "Description" +msgstr "Описание" + +#: ml_model/models.py:99 msgid "Runner" msgstr "Раннер" -#: ml_model/models.py:66 +#: ml_model/models.py:102 msgid "Output Type" msgstr "Тип исходящего контента" -#: ml_model/models.py:68 ml_model/models.py:241 +#: ml_model/models.py:104 ml_model/models.py:303 msgid "Enabled" msgstr "Включен" -#: ml_model/models.py:75 +#: ml_model/models.py:111 msgid "Runner is missing" msgstr "Раннер не найден" -#: ml_model/models.py:93 ml_model/models.py:119 ml_model/models.py:162 -#: ml_model/models.py:210 ml_model/models.py:236 +#: ml_model/models.py:133 ml_model/models.py:159 ml_model/models.py:202 +#: ml_model/models.py:270 ml_model/models.py:296 msgid "Deployment" msgstr "Деплоймент" -#: ml_model/models.py:94 +#: ml_model/models.py:134 msgid "Deployments" msgstr "Деплойменты" -#: ml_model/models.py:222 stories/models.py:36 +#: ml_model/models.py:140 ml_model/models.py:425 msgid "Image" msgstr "Картинка" -#: ml_model/models.py:101 +#: ml_model/models.py:141 msgid "PDF" msgstr "PDF" -#: ml_model/models.py:102 +#: ml_model/models.py:142 msgid "DOCX" msgstr "DOCX" -#: ml_model/models.py:103 +#: ml_model/models.py:143 msgid "DOC" msgstr "DOC" -#: ml_model/models.py:104 +#: ml_model/models.py:144 msgid "Text File (Notebook)" msgstr "Текстовый файл (Блокнот)" -#: ml_model/models.py:105 +#: ml_model/models.py:145 msgid "ZIP Archive" msgstr "ZIP архив" -#: ml_model/models.py:106 +#: ml_model/models.py:146 ml_model/models.py:427 msgid "Audio" msgstr "Аудио" -#: ml_model/models.py:112 ml_model/models.py:148 ml_model/models.py:296 -#: ml_model/models.py:371 payments/models/promocode.py:41 +#: ml_model/models.py:152 ml_model/models.py:188 ml_model/models.py:374 +#: ml_model/models.py:453 payments/models/promocode.py:41 msgid "Type" msgstr "Тип" -#: ml_model/models.py:114 ml_model/models.py:157 ml_model/models.py:276 +#: ml_model/models.py:154 ml_model/models.py:197 ml_model/models.py:352 msgid "Required" msgstr "Обязательный" -#: ml_model/models.py:124 +#: ml_model/models.py:164 #, python-format msgid "%(input_type)s input of %(deployment_title)s" msgstr "Входящий поток типа %(input_type)s деплоймента %(deployment_title)s" -#: ml_model/models.py:124 -#, python-format -msgid "%(model_title)s | %(input_type)s" -msgstr "%(model_title)s | %(input_type)s" - -#: ml_model/models.py:130 +#: ml_model/models.py:170 #, fuzzy #| msgid "Model Input" msgid "Input" msgstr "Модель" -#: ml_model/models.py:131 +#: ml_model/models.py:171 #, fuzzy #| msgid "Model Inputs" msgid "Inputs" msgstr "Входящий поток модели" -#: ml_model/models.py:137 +#: ml_model/models.py:177 msgid "Integer" msgstr "Целое число" -#: ml_model/models.py:138 +#: ml_model/models.py:178 msgid "Float" msgstr "Вещественное число" -#: ml_model/models.py:139 +#: ml_model/models.py:179 msgid "String" msgstr "Строка" -#: ml_model/models.py:140 +#: ml_model/models.py:180 #, fuzzy #| msgid "Invoices" msgid "Choices" msgstr "Списания" -#: ml_model/models.py:141 +#: ml_model/models.py:181 msgid "Float range" msgstr "Вещественный диапазон" -#: ml_model/models.py:142 +#: ml_model/models.py:182 msgid "Integer range" msgstr "Целочисленный диапазон" -#: ml_model/models.py:143 +#: ml_model/models.py:183 msgid "Logical" msgstr "Логический" -#: ml_model/models.py:153 +#: ml_model/models.py:193 msgid "Values" msgstr "Значения" -#: ml_model/models.py:154 -msgid "These values can contain different interfaces and default value optional" -msgstr "Значения могут содержать различные интерфейс и, опционально, значение по " +#: ml_model/models.py:194 +msgid "" +"These values can contain different interfaces and default value optional" +msgstr "" +"Значения могут содержать различные интерфейс и, опционально, значение по " "умолчанию" -#: ml_model/models.py:156 ml_model/models.py:275 +#: ml_model/models.py:196 ml_model/models.py:351 msgid "Hidden" msgstr "Скрытый" -#: ml_model/models.py:167 +#: ml_model/models.py:210 +msgid "Key \"default\" is required" +msgstr "" + +#: ml_model/models.py:213 #, fuzzy, python-format #| msgid "Parameter of %(model_title)s" msgid "Parameter \"%(key)s\" of %(deployment_title)s" msgstr "Параметр %(model_title)s" -#: ml_model/models.py:173 ml_model/models.py:272 +#: ml_model/models.py:219 ml_model/models.py:348 msgid "Parameter" msgstr "Параметр" -#: ml_model/models.py:174 +#: ml_model/models.py:220 msgid "Parameters" msgstr "Параметры" -#: ml_model/models.py:180 +#: ml_model/models.py:226 msgid "Fixed" msgstr "Фикса" -#: ml_model/models.py:181 +#: ml_model/models.py:227 msgid "Per generation second" msgstr "За секунду генерации" -#: ml_model/models.py:182 +#: ml_model/models.py:228 msgid "Per one text token" msgstr "За один текстовый токен" -#: ml_model/models.py:183 +#: ml_model/models.py:229 msgid "Per image pixel" msgstr "За один пиксель" -#: ml_model/models.py:186 +#: ml_model/models.py:232 msgid "By input data" msgstr "По входящим данным" -#: ml_model/models.py:187 +#: ml_model/models.py:233 msgid "By output data" msgstr "По исходящим данным" -#: ml_model/models.py:188 -msgid "By all data" -msgstr "По всем данным" - -#: ml_model/models.py:193 +#: ml_model/models.py:243 msgid "Strategy" msgstr "Стратегия" -#: ml_model/models.py:198 +#: ml_model/models.py:250 msgid "Interaction Type" msgstr "Тип взаимодействия" -#: ml_model/models.py:203 payments/models/invoice.py:19 +#: ml_model/models.py:257 +#, fuzzy +#| msgid "Interaction Type" +msgid "Content Type" +msgstr "Тип взаимодействия" + +#: ml_model/models.py:263 payments/models/invoice.py:19 msgid "Cost" msgstr "Цена" -#: ml_model/models.py:204 +#: ml_model/models.py:264 msgid "In RUB, per specified strategy" msgstr "В рублях, за указанную стратегию" -#: ml_model/models.py:215 +#: ml_model/models.py:275 #, python-format -msgid "Payment Rule \"%(strategy)s\"/\"%(interaction_type)s\" of " +msgid "" +"Payment Rule \"%(strategy)s\"/\"%(interaction_type)s\" of " +"%(deployment_title)s" +msgstr "" +"Платежное правило \"%(strategy)s\"/\"%(interaction_type)s\" деплоймента " "%(deployment_title)s" -msgstr "Платежное правило \"%(strategy)s\"/\"%(interaction_type)s\" деплоймента %(deployment_title)s" - -#: ml_model/models.py:327 -msgid "Coefficient" -msgstr "Коэффициент" - -#: ml_model/models.py:328 -msgid "Cost multiplier" -msgstr "Цена" - -#: ml_model/models.py:335 -msgid "Rate" -msgstr "Ставка" -#: ml_model/models.py:339 +#: ml_model/models.py:282 msgid "Payment Rule" msgstr "Платежное правило" -#: ml_model/models.py:340 +#: ml_model/models.py:283 msgid "Payment Rules" msgstr "Платежные правила" -#: ml_model/models.py:245 -msgid "Inference cannot be available when parent Deployment is disabled" -msgstr "Инференс не может быть доступен, когда родительский Деплоймент выключен" +#: ml_model/models.py:307 +#, fuzzy +#| msgid "Inference cannot be available when parent Deployment is disabled" +msgid "Inference cannot be available when related Deployment is disabled" +msgstr "" +"Инференс не может быть доступен, когда родительский Деплоймент выключен" -#: ml_model/models.py:274 +#: ml_model/models.py:350 msgid "Value" msgstr "Значение" -#: ml_model/models.py:280 +#: ml_model/models.py:358 msgid "Parameter must be hidden cause parent is hidden" msgstr "Параметр должен быть скрыт, потому что родительский также скрыт" -#: ml_model/models.py:282 +#: ml_model/models.py:360 msgid "Parameter must be required cause parent is required" -msgstr "Параметр должен быть обязательным, потому что родительский также обязателен" +msgstr "" +"Параметр должен быть обязательным, потому что родительский также обязателен" -#: ml_model/models.py:285 +#: ml_model/models.py:363 msgid "Overriden Parameter" msgstr "Переопределенный параметр" -#: ml_model/models.py:286 +#: ml_model/models.py:364 msgid "Overriden Parameters" msgstr "Переопределенные параметры" -#: ml_model/models.py:292 +#: ml_model/models.py:370 msgid "Addition" msgstr "Сложение" -#: ml_model/models.py:293 +#: ml_model/models.py:371 msgid "Multiplication" msgstr "Умножение" -#: ml_model/models.py:308 +#: ml_model/models.py:373 +msgid "Coefficient" +msgstr "Коэффициент" + +#: ml_model/models.py:386 #, python-format msgid "Payment Bias of %(inference_title)s" msgstr "Платежный сдвиг %(inference_title)s" -#: ml_model/models.py:311 ml_model/models.py:312 +#: ml_model/models.py:389 ml_model/models.py:390 msgid "Payment Bias" msgstr "Платежные сдвиг" -#: ml_model/models.py:316 +#: ml_model/models.py:394 msgid "Generation time" msgstr "Время генерации" -#: ml_model/models.py:317 +#: ml_model/models.py:395 msgid "Tokens cost" msgstr "Стоимость в токенах" -#: ml_model/models.py:328 +#: ml_model/models.py:406 #, python-format msgid "Tracking Record created at %(created_at)s of %(inference_title)s" msgstr "" -#: ml_model/models.py:334 +#: ml_model/models.py:412 msgid "Tracking Record" msgstr "Отслеживающая запись" -#: ml_model/models.py:335 +#: ml_model/models.py:413 msgid "Tracking Records" msgstr "Отслеживающие записи" -#: ml_model/models.py:345 +#: ml_model/models.py:423 msgid "Chat-bots" msgstr "Чат-боты" -#: ml_model/models.py:353 +#: ml_model/models.py:426 +msgid "Video" +msgstr "" + +#: ml_model/models.py:434 msgid "Alternative Titles" msgstr "Альтернативные названия" -#: ml_model/models.py:359 +#: ml_model/models.py:440 msgid "Fill automatically, don't touch" msgstr "Заполняется автоматически, не трогать" -#: ml_model/models.py:368 +#: ml_model/models.py:449 msgid "Avatar" msgstr "Аватар" -#: ml_model/models.py:387 +#: ml_model/models.py:456 +#, fuzzy +#| msgid "Type" +msgid "Types" +msgstr "Тип" + +#: ml_model/models.py:485 msgid "Neuron Model" msgstr "Нейронная Модель" -#: ml_model/models.py:401 -msgid "Descriptor" -msgstr "Дескриптор" +#: ml_model/runners/dummy.py:19 +#, fuzzy +#| msgid "Missing required parameter: 'messages'" +msgid "Missing required parameter - Message (key=message,type=str)" +msgstr "Отсутствует обязательный параметр: 'messages'" + +#: ml_model/runners/dummy.py:24 +msgid "" +"Missing required parameter - Time to First Token (in seconds) " +"(key=ttft,type=integer_range)" +msgstr "" -#: ml_model/models.py:407 -#, python-format -msgid "Instruction of %(model_title)s" -msgstr "Инструкция %(model_title)s" +#: ml_model/runners/dummy.py:32 +msgid "" +"Missing required parameter - Time Between Tokens (in seconds) " +"(key=tbt,type=integer_range)" +msgstr "" -#: ml_model/models.py:410 -msgid "Model Instruction" -msgstr "Инструкция Модели" +#: ml_model/runners/dummy.py:39 +msgid "" +"Missing required parameter - Token Throughput Rate (key=ttpr,type=list[int])" +msgstr "" -#: ml_model/models.py:411 -msgid "Model Instructions" -msgstr "Инструкции Моделей" +#: ml_model/runners/dummy.py:68 +#, fuzzy +#| msgid "Missing required parameter: 'messages'" +msgid "Missing required parameter - URL (key=url,type=string)" +msgstr "Отсутствует обязательный параметр: 'messages'" + +#: ml_model/runners/dummy.py:72 +msgid "" +"Missing required parameter - Chunk size (key=chunk_size,type=integer_range)" +msgstr "" -#: ml_model/selectors/ml_models_selector.py:81 -msgid "no model by this id" -msgstr "Не найдено моделей по этому ID" +#: ml_model/runners/dummy.py:79 +msgid "" +"Missing required parameter - Time to First Chunk (in seconds) " +"(key=ttfc,type=integer_range)" +msgstr "" -#: ml_model/services/chatgpt.py:125 -msgid "Unable to recognize the image. (Supported formats are PNG, JPG, JPEG)" -msgstr "Невозможно распознать изображение. (Поддерживаемые форматы: PNG, JPG, JPEG)" +#: ml_model/runners/dummy.py:87 +msgid "" +"Missing required parameter - Time Between Chunks (in seconds) " +"(key=tbc,type=integer_range)" +msgstr "" -#: ml_model/services/chatgpt.py:150 -msgid "No matching version found" -msgstr "Соответствующая версия не найдена" +#: ml_model/runners/falai.py:19 ml_model/runners/openai.py:33 +#: ml_model/runners/replicate.py:28 +#, fuzzy +#| msgid "Missing required parameter: 'messages'" +msgid "Missing required parameter - Model (key=model,type=string)" +msgstr "Отсутствует обязательный параметр: 'messages'" -#: ml_model/services/inference.py:49 -msgid "Payment rules are missing; Inference: {}" +#: ml_model/runners/falai.py:24 ml_model/runners/replicate.py:33 +msgid "Missing required parameter - Model Owner (key=model_owner,type=string)" msgstr "" -#: ml_model/services/inference.py:134 +#: ml_model/services/inference.py:108 +#, python-format +msgid "Payment rules are missing; Inference: %s" +msgstr "" + +#: ml_model/services/inference.py:249 #, fuzzy #| msgid "No matching version found" msgid "No tracking records found" msgstr "Соответствующая версия не найдена" -#: ml_model/services/inference.py:171 -msgid "Unable to predict price" +#: payments/admin.py:33 payments/admin.py:67 payments/admin.py:87 +msgid "You can search by user email, exacted company name" msgstr "" +"Вы можете осуществлять поиск по e-mail пользователя, точному названию " +"компании" + +#: payments/admin.py:38 payments/admin.py:92 +msgid "Missing" +msgstr "Отсутствующий" -#: ml_model/services/upscaleai.py:80 -msgid "No image given for improving" -msgstr "Нет изображения для улучшения" +#: payments/admin.py:95 +msgid "Model" +msgstr "Модель" #: payments/apps.py:9 payments/models/payment.py:60 msgid "Payments" @@ -1077,6 +1107,10 @@ msgstr "" "Баланс: %(balance).2f токенов; Нужно: %(required)s токенов; Нужно еще: " "%(needed)s токенов" +#: payments/exceptions/payer_not_found.py:6 +msgid "The payer does not exist" +msgstr "Плательщик не существует" + #: payments/models/invoice.py:23 msgid "Generative Model" msgstr "Генеративная модель" @@ -1205,10 +1239,6 @@ msgstr "Платежный метод" msgid "Payment Methods" msgstr "Платежные методы" -#: payments/selectors/model_payment_selector.py:25 -msgid "Messages for this model are not registered in a selector" -msgstr "" - #: payments/selectors/payment_plan_selector.py:32 msgid "Business accounts are not allowed to make purchases" msgstr "Сотрудники не могут производить покупки" @@ -1261,42 +1291,6 @@ msgstr "Пользовательский репорт" msgid "User Reports" msgstr "Пользовательские репорты" -#: stories/apps.py:8 stories/models.py:26 -msgid "Stories" -msgstr "Истории" - -#: stories/models.py:13 -msgid "Is published" -msgstr "Опубликовано" - -#: stories/models.py:25 stories/models.py:40 -msgid "Story" -msgstr "История" - -#: stories/models.py:49 stories/models.py:59 -msgid "Page" -msgstr "Страница" - -#: stories/models.py:50 -msgid "Pages" -msgstr "Страницы" - -#: stories/models.py:54 -msgid "Label" -msgstr "Метка" - -#: stories/models.py:55 -msgid "Redirect URL" -msgstr "URL перехода" - -#: stories/models.py:64 -msgid "Widget" -msgstr "Виджет" - -#: stories/models.py:65 -msgid "Widgets" -msgstr "Виджеты" - #: tools/apps.py:8 msgid "Tools" msgstr "Инструменты" @@ -1317,13 +1311,10 @@ msgstr "Публичный API" msgid "Media" msgstr "Медиа" -#: tools/apps.py:38 -msgid "Feed" -msgstr "Шейр пользователей" - -#: tools/chats/models.py:13 tools/public_api/models.py:45 -msgid "Is deleted" -msgstr "Удален" +#: tools/chats/apis.py:142 tools/media/apis.py:151 +#: tools/public_api/views/base.py:69 +msgid "Enabled inference not found in model" +msgstr "" #: tools/chats/models.py:17 #, python-format @@ -1334,6 +1325,10 @@ msgstr "Чат %(id)s" msgid "Chat" msgstr "Чат" +#: tools/chats/services/chat.py:21 +msgid "New chat" +msgstr "" + #: tools/public_api/exceptions.py:7 msgid "Upgrade token limit on your api-key" msgstr "Необходимо повысить лимит токенов у API-ключа" @@ -1354,23 +1349,39 @@ msgstr "API Ключ" msgid "API Keys" msgstr "API Ключи" -#: tools/public_api/views/base.py:58 +#: tools/public_api/views/base.py:49 msgid "Key limit exceeded" msgstr "Превышен лимит по ключу" -#: tools/public_api/views/base.py:63 +#: tools/public_api/views/base.py:55 msgid "Model is blocked by outdating or temporary block, please retry later" msgstr "" "Модель заблокирована, т.к закончила обновляться или временно заблокирована, " "попробуйте позже" -#: tools/public_api/views/base.py:90 -msgid "" -"Error occured when create generation. It may cause NSFW-content not allowed, " -"retry again" -msgstr "" -"Случилась ошибка во время генерации. Она может возникать из-за того, что " -"NSFW-контент запрещен. Попробуйте снова" +#~ msgid "Account is already confirmed" +#~ msgstr "Аккаунт уже подтвержден" + +#~ msgid "Wrong username" +#~ msgstr "Неверное имя пользователя" + +#~ msgid "The model is not responding" +#~ msgstr "Модель не отвечает" + +#~ msgid "The length of the context has been exceeded." +#~ msgstr "Длина контекста превышена." + +#~ msgid "Jinja template not found" +#~ msgstr "Jinja-шаблон не найден" + +#~ msgid "There was an unknown error while rendering a template" +#~ msgstr "При рендеринге шаблона произошла неизвестная ошибка" + +#~ msgid "Category" +#~ msgstr "Категория" + +#~ msgid "Categories" +#~ msgstr "Категории" #~ msgid "Model Tag" #~ msgstr "Тег модели" @@ -1378,50 +1389,152 @@ msgstr "" #~ msgid "Model Tags" #~ msgstr "Теги модели" +#~ msgid "Settings" +#~ msgstr "Настройки" + +#, python-format +#~ msgid "Settings of %(model_title)s" +#~ msgstr "Настройки %(model_title)s" + +#, python-format +#~ msgid "%(model_title)s | %(version_name)s" +#~ msgstr "%(model_title)s | %(version_name)s" + +#~ msgid "Model Version" +#~ msgstr "Версия Модели" + +#~ msgid "Model Versions" +#~ msgstr "Версии Модели" + +#~ msgid "Versions" +#~ msgstr "Версии" + +#~ msgid "Link to versions" +#~ msgstr "Привязка к версиям" + +#, python-format +#~ msgid "%(model_title)s | %(input_type)s" +#~ msgstr "%(model_title)s | %(input_type)s" + +#~ msgid "By all data" +#~ msgstr "По всем данным" + +#~ msgid "Cost multiplier" +#~ msgstr "Цена" + +#~ msgid "Rate" +#~ msgstr "Ставка" + +#~ msgid "Descriptor" +#~ msgstr "Дескриптор" + +#, python-format +#~ msgid "Instruction of %(model_title)s" +#~ msgstr "Инструкция %(model_title)s" + +#~ msgid "Model Instruction" +#~ msgstr "Инструкция Модели" + +#~ msgid "Model Instructions" +#~ msgstr "Инструкции Моделей" + +#~ msgid "no model by this id" +#~ msgstr "Не найдено моделей по этому ID" + +#~ msgid "" +#~ "Unable to recognize the image. (Supported formats are PNG, JPG, JPEG)" +#~ msgstr "" +#~ "Невозможно распознать изображение. (Поддерживаемые форматы: PNG, JPG, " +#~ "JPEG)" + +#~ msgid "No matching version found" +#~ msgstr "Соответствующая версия не найдена" + +#~ msgid "No image given for improving" +#~ msgstr "Нет изображения для улучшения" + +#~ msgid "Model data cannot be retrieved" +#~ msgstr "Невозможно получить данные модели" + +#~ msgid "Stories" +#~ msgstr "Истории" + +#~ msgid "Is published" +#~ msgstr "Опубликовано" + +#~ msgid "Story" +#~ msgstr "История" + +#~ msgid "Page" +#~ msgstr "Страница" + +#~ msgid "Pages" +#~ msgstr "Страницы" + +#~ msgid "Label" +#~ msgstr "Метка" + +#~ msgid "Redirect URL" +#~ msgstr "URL перехода" + +#~ msgid "Widget" +#~ msgstr "Виджет" + +#~ msgid "Widgets" +#~ msgstr "Виджеты" + +#~ msgid "Feed" +#~ msgstr "Шейр пользователей" + +#~ msgid "" +#~ "Error occured when create generation. It may cause NSFW-content not " +#~ "allowed, retry again" +#~ msgstr "" +#~ "Случилась ошибка во время генерации. Она может возникать из-за того, что " +#~ "NSFW-контент запрещен. Попробуйте снова" + #~ msgid "List" #~ msgstr "Список" -msgid "Unknown file format" -msgstr "Неизвестный формат файла" +#~ msgid "Token prefix is missing" +#~ msgstr "Отсутствует префикс токена" -msgid "Access token is expired" -msgstr "Срок действия токена доступа истек" +#~ msgid "The request must not be empty" +#~ msgstr "Запрос не должен быть пустым" -msgid "Token prefix is missing" -msgstr "Отсутствует префикс токена" +#~ msgid "You must provide a model parameter" +#~ msgstr "Необходимо указать параметр 'model'" -msgid "Access token expired or does not exist" -msgstr "Токен доступа просрочен или не существует" +#~ msgid "Model not found" +#~ msgstr "Модель не найдена" -msgid "Model data cannot be retrieved" -msgstr "Невозможно получить данные модели" +#~ msgid "Achievement" +#~ msgstr "Достижение" -msgid "The payer does not exist" -msgstr "Плательщик не существует" +#~ msgid "Achievements" +#~ msgstr "Достижения" -msgid "The request must not be empty" -msgstr "Запрос не должен быть пустым" +#~ msgid "Issued at" +#~ msgstr "Когда выдано" -msgid "Your request was blocked by our moderation system" -msgstr "Ваш запрос был заблокирован нашей системой модерации" - -msgid "Image size %dx%d is not supported. Please rotate image to %dx%d" -msgstr "Размер изображения %dx%d не поддерживается. Пожалуйста, переверните до %dx%d" +#, python-format +#~ msgid "Achievement %(achievement_title)s пользователя %(username)s" +#~ msgstr "Достижение %(achievement_title)s пользователя %(username)s" -msgid "Image size %dx%d is not supported. Required size: %dx%d" -msgstr "Размер изображения %dx%d не поддерживается. Требуемый размер: %dx%d" +#~ msgid "Issued achievement" +#~ msgstr "Выданное достижение" -msgid "You must provide a model parameter" -msgstr "Необходимо указать параметр 'model'" +#~ msgid "Points" +#~ msgstr "Поинты" -msgid "Missing required parameter: 'messages'" -msgstr "Отсутствует обязательный параметр: 'messages'" +#~ msgid "Message" +#~ msgstr "Сообщение" -msgid "The neuron model does not exist" -msgstr "Нейронная модель не существует" +#~ msgid "Detail" +#~ msgstr "Подробности" -msgid "Email token not found. Please contact support" -msgstr "E-mail токен не найден. Пожалуйста, свяжитесь со службой поддержки" +#~ msgid "Message error" +#~ msgstr "Ошибка сообщения" -msgid "Model not found" -msgstr "Модель не найдена" +#~ msgid "Message errors" +#~ msgstr "Ошибки сообщения" @@ -31,37 +31,25 @@ class RequestBlocked(Exception): return _('Your request was blocked by our moderation system') -class UnsupportedSize(Exception): - def __init__(self, current_size: tuple[int, int], required_size: tuple[int, int]): - self.current_size = current_size - self.required_size = required_size - +class DeploymentDisabled(Exception): def __str__(self): - return _('Payment Rule not implemented') - - -class RequestBlocked(Exception): - def __str__(self): - return _('Your request was blocked by our moderation system') + return _('The model is currently disabled. Please try again later.') class UnsupportedSize(Exception): def __init__(self, current_size: tuple[int, int], required_size: tuple[int, int]): - self.current_size = current_size - self.required_size = required_size + self.current_size = dict(zip(('cw', 'ch'), current_size)) + self.required_size = dict(zip(('rw', 'rh'), required_size)) def __str__(self): - if self.current_size == self.required_size[::-1]: - return _('Image size %dx%d is not supported. Please rotate image to %dx%d') % ( - *self.current_size, - *self.required_size, - ) - else: - return _('Image size %dx%d is not supported. Required size: %dx%d') % ( - *self.current_size, - *self.required_size, - ) - + if tuple(self.current_size.values()) == tuple(reversed(self.required_size.values())): + return _( + 'Image size %(cw)dx%(ch)d is not supported. ' + 'Please rotate image to %(rw)dx%(rh)d' + ) % (self.current_size | self.required_size) + return _( + 'Image size %(cw)sx%(ch)s is not supported. Required size: %(rw)sx%(rh)s' + ) % (self.current_size | self.required_size) class ScraperDoesNotExists(Exception): def __str__(self): @@ -1,12 +1,10 @@ from decimal import Decimal from django.contrib import admin -from django.contrib.admin import DateFieldListFilter from django.db import models -from django.db.models import Count, QuerySet, Sum +from django.db.models import Count, Sum from django.db.models.functions import Coalesce -from django.http import HttpResponse -from openpyxl import Workbook +from django.utils.translation import gettext_lazy as _ from authentication.admin import CustomUserModelAdmin from payments.models import ( @@ -23,47 +21,22 @@ from payments.models.referral_account import ReferralAccount, ReferralInvite @admin.register(Payment) class PaymentAdmin(admin.ModelAdmin): + list_display = ['uid', '_user', 'created_at', 'status'] + raw_id_fields = ['user'] + date_hierarchy = 'created_at' + search_fields = [ 'uid', - 'amount', - 'created_at', - *(f'user__{field}' for field in CustomUserModelAdmin.search_fields), - ] - raw_id_fields = ['user'] - list_filter = [ - ('created_at', DateFieldListFilter), - ] - actions = [ - 'download_report', + 'user__host_account__company_name', + 'user__business_account__parent_company__company_name', ] + search_help_text = _('You can search by user email, exacted company name') - @admin.action(description='Скачать отчет') - def download_report(self, request, qs: QuerySet[Payment]): - wb = Workbook() - sheet = wb.active - sheet.append( - [ - 'Когда создан', - 'Email пользователя', - 'Статус', - 'Сумма', - 'Описание к платежу', - ] - ) - for s in qs: - sheet.append( - [ - str(s.created_at), - s.user.email, - s.status, - s.amount, - s.description, - ] - ) - response = HttpResponse(content_type='application/ms-excel') - response['Content-Disposition'] = 'attachment; filename=payments_info.xlsx' - wb.save(response) - return response + @admin.display(description=_('User')) + def _user(self, obj: Payment): + if obj.user is None: + return _('Missing') + return str(obj.user) @admin.register(PaymentPlan) @@ -84,12 +57,14 @@ class PaymentPlanAdmin(admin.ModelAdmin): @admin.register(PaymentPlanUserInfo) class PaymentPlanUserInfoAdmin(admin.ModelAdmin): list_display = ['user', 'current_token_balance', 'plan', 'updated_at'] + raw_id_fields = ['user'] + search_fields = [ - 'current_token_balance', - 'plan__price', - *(f'user__{field}' for field in CustomUserModelAdmin.search_fields), + 'uid', + 'user__host_account__company_name', + 'user__business_account__parent_company__company_name', ] - raw_id_fields = ['user'] + search_help_text = _('You can search by user email, exacted company name') @admin.register(UserPaymentMethod) @@ -104,54 +79,22 @@ class InvoiceAdmin(admin.ModelAdmin): raw_id_fields = ['user', 'message', 'model'] search_fields = [ - 'user__username', + 'user__email', 'user__host_account__company_name', 'user__business_account__parent_company__company_name', ] - search_help_text = 'Можно искать по юзернейму, названию компании' - - actions = [ - 'download_report', - ] - - list_filter = [] + search_help_text = _('You can search by user email, exacted company name') - @admin.display(description='Пользователь') + @admin.display(description=_('User')) def _user(self, obj): if obj.user is None: - return 'Ошибка' - return obj.user.email + return _('Missing') + return str(obj.user) - @admin.display(description='Модель') + @admin.display(description=_('Model')) def _model(self, obj): - return obj.model.title - - @admin.action(description='Скачать отчет') - def download_report(self, request, qs: QuerySet[Invoice]): - wb = Workbook() - sheet = wb.active - sheet.append( - [ - 'Дата списания', - 'Email пользователя', - 'Название модели', - 'Сумма', - ] - ) - for s in qs: - sheet.append( - [ - str(s.created_at), - s.user.email, - s.model.title, - s.cost, - ] - ) - response = HttpResponse(content_type='application/ms-excel') - response['Content-Disposition'] = 'attachment; filename=invoices_info.xlsx' - wb.save(response) - return response + return str(obj.model) @admin.register(PromoCode) @@ -23,7 +23,7 @@ class ReferralSerializer(serializers.ModelSerializer): class Meta: model = get_user_model() - fields = ('username', 'joined_at', 'profile_picture_link') + fields = ('joined_at', 'profile_picture_link') class ReferralAccountSerializer(serializers.ModelSerializer): @@ -21,7 +21,7 @@ class ErrorReportService: image_names = [] if images := serializer.validated_data.get('images', False): for img in images: - file, filename = create_original_image(img, self.user.username, datetime.now().timestamp()) + file, filename = create_original_image(img, self.user.email, datetime.now().timestamp()) res_name = MinIOService().put_object(file, filename, 'air-errors') image_names.append(res_name) report = ErrorReport.objects.create(