diff --git a/apps/account/admin/location.py b/apps/account/admin/location.py index c1740e3..60cb326 100644 --- a/apps/account/admin/location.py +++ b/apps/account/admin/location.py @@ -54,6 +54,7 @@ class LocationHistoryAdmin(ModelAdmin): return instance.at_time.strftime("%Y-%m-%d %H:%M") if instance.at_time else "-" + # Register with both admin sites project_admin_site.register(LocationHistory, LocationHistoryAdmin) dovoodi_admin_site.register(LocationHistory, LocationHistoryAdmin) \ No newline at end of file diff --git a/apps/account/admin/notification.py b/apps/account/admin/notification.py index b17d676..f62ff66 100644 --- a/apps/account/admin/notification.py +++ b/apps/account/admin/notification.py @@ -1,12 +1,14 @@ -from ajaxdatatable.admin import AjaxDatatable - -from apps.account.models import User, Notification - -@admin.register(Notification) -class NotificationAdmin(AjaxDatatable): - list_display = ('title', 'user', 'is_read', 'created_at') - list_filter = ('is_read', 'created_at') - search_fields = ('title', 'message', 'user__fullname') - list_editable = ('is_read',) - ordering = ('-created_at',) - autocomplete_fields = ['user',] \ No newline at end of file +from django.contrib import admin +from django.utils.translation import gettext_lazy as _ +from ajaxdatatable.admin import AjaxDatatable + +from apps.account.models import User, Notification + +@admin.register(Notification) +class NotificationAdmin(AjaxDatatable): + list_display = ('title', 'user', 'is_read', 'created_at') + list_filter = ('is_read', 'created_at') + search_fields = ('title', 'message', 'user__fullname') + list_editable = ('is_read',) + ordering = ('-created_at',) + autocomplete_fields = ['user',] \ No newline at end of file diff --git a/apps/account/admin/professor.py b/apps/account/admin/professor.py index 82fecc0..0014d42 100644 --- a/apps/account/admin/professor.py +++ b/apps/account/admin/professor.py @@ -19,7 +19,7 @@ from apps.account.models import ProfessorUser class ProfessorUserCreationForm(UserCreationForm): phone_number = PhoneNumberField( - help_text="Enter the phone number in international format. Example: +989012023212", + help_text=_("Enter the phone number in international format. Example: +989012023212"), required=False ) @@ -42,11 +42,11 @@ class ProfessorUserAdmin(UserAdmin, AjaxDatatable): 'classes': ('wide',), 'fields': ('fullname', 'email', 'phone_number',), }), - ('other', { + (_('other'), { 'classes': ('wide',), 'fields': ('avatar', 'info', 'skill'), }), - ('Password', { + (_('Password'), { 'classes': ('wide',), 'fields': ('password1', 'password2'), }), @@ -72,7 +72,7 @@ class ProfessorUserAdmin(UserAdmin, AjaxDatatable): if existing_user: # If user exists and is already a professor, show error if existing_user.user_type == User.UserType.PROFESSOR: - messages.error(request, f"A professor with the email {email} already exists.") + messages.error(request, _("A professor with the email {email} already exists.").format(email=email)) return # اضافه کردن نقش professor بدون حذف نقش‌های قبلی @@ -93,7 +93,7 @@ class ProfessorUserAdmin(UserAdmin, AjaxDatatable): existing_user.save() # Show success message - messages.success(request, f"The user with email {email} has been converted to a professor.") + messages.success(request, _("The user with email {email} has been converted to a professor.").format(email=email)) # Set obj to None to prevent further processing obj = None @@ -106,11 +106,12 @@ class ProfessorUserAdmin(UserAdmin, AjaxDatatable): obj.add_role('professor') super().save_model(request, obj, form, change) - @admin.display(description='Phone Number') + @admin.display(description=_('Phone Number')) def _phone_number(self, obj): return obj.phone_number + def get_readonly_fields(self, request, obj=None): """ Restrict the ability to modify groups to superusers only. diff --git a/apps/account/admin/student.py b/apps/account/admin/student.py index a7cc389..96f1213 100644 --- a/apps/account/admin/student.py +++ b/apps/account/admin/student.py @@ -1,7 +1,7 @@ from django.contrib import admin from django.contrib.auth.forms import UserChangeForm, UsernameField from django.contrib.auth.admin import UserAdmin -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, ngettext from rest_framework.authtoken.models import TokenProxy from ajaxdatatable.admin import AjaxDatatable from unfold.admin import TabularInline, StackedInline @@ -29,11 +29,11 @@ class StudentUserAdmin(UserAdmin, AjaxDatatable): 'classes': ('wide',), 'fields': ('fullname', 'email', 'phone_number',), }), - ('other', { + (_('other'), { 'classes': ('wide',), 'fields': ('avatar', 'info'), }), - ('Password', { + (_('Password'), { 'classes': ('wide',), 'fields': ('password1', 'password2'), }), @@ -48,7 +48,7 @@ class StudentUserAdmin(UserAdmin, AjaxDatatable): }), (_('Important dates'), {'fields': ('last_login', 'date_joined', 'fcm')}), ) - @admin.display(description='Phone Number') + @admin.display(description=_('Phone Number')) def _phone_number(self, obj): return obj.phone_number @@ -56,7 +56,8 @@ class StudentUserAdmin(UserAdmin, AjaxDatatable): def enrolled_courses_count(self, obj): """نمایش تعداد دوره‌های شرکت کرده""" count = obj.participated_courses.filter(is_active=True).count() - return f"{count} دوره" + return ngettext("{count} course", "{count} courses", count).format(count=count) + def get_queryset(self, request): diff --git a/apps/account/admin/user.py b/apps/account/admin/user.py index 049aace..91c5eba 100644 --- a/apps/account/admin/user.py +++ b/apps/account/admin/user.py @@ -5,7 +5,7 @@ from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin from django.contrib.auth.models import Group from django.db import models from django.utils.html import format_html -from django.utils.translation import gettext_lazy as _ +from django.utils.translation import gettext_lazy as _, ngettext from django.templatetags.static import static from rest_framework.authtoken.models import TokenProxy @@ -278,7 +278,7 @@ class StudentUserAdmin(UserAdmin): birthdate = instance.birthdate age = today.year - birthdate.year - ((today.month, today.day) < (birthdate.month, birthdate.day)) formatted_date = birthdate.strftime("%Y-%m-%d") - return format_html('{}', f"Born on {formatted_date}", age) + return format_html('{}', _("Born on {date}").format(date=formatted_date), age) @display(description=_("Courses"), dropdown=True) def courses_count(self, instance: StudentUser): @@ -304,11 +304,12 @@ class StudentUserAdmin(UserAdmin): return "-" return { - "title": f"{total} {_('courses')}", + "title": ngettext("{total} course", "{total} courses", total).format(total=total), "items": items, "striped": True, } + def get_queryset(self, request): return super().get_queryset(request).prefetch_related( "participated_courses", @@ -443,11 +444,12 @@ class ProfessorUserAdmin(UserAdmin): return "-" return { - "title": f"{total} {_('courses')}", + "title": ngettext("{total} course", "{total} courses", total).format(total=total), "items": items, "striped": True, } + def get_queryset(self, request): return super().get_queryset(request).prefetch_related("courses") @@ -466,7 +468,7 @@ class GroupAdmin(BaseGroupAdmin, ModelAdmin): @display(description=_("Permissions")) def permissions_count(self, obj): count = obj.permissions.count() - return f"{count} {_('permissions')}" if count > 0 else "-" + return ngettext("{count} permission", "{count} permissions", count).format(count=count) if count > 0 else "-" # ========================================================= diff --git a/apps/account/migrations/0003_alter_clientuser_options_and_more.py b/apps/account/migrations/0003_alter_clientuser_options_and_more.py new file mode 100644 index 0000000..ee01b33 --- /dev/null +++ b/apps/account/migrations/0003_alter_clientuser_options_and_more.py @@ -0,0 +1,127 @@ +# Generated by Django 5.2.12 on 2026-05-04 12:53 + +import dj_language.field +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0002_alter_user_email_alter_user_username'), + ('dj_language', '0002_auto_20220120_1344'), + ] + + operations = [ + migrations.AlterModelOptions( + name='clientuser', + options={'ordering': ('-id',), 'verbose_name': 'User', 'verbose_name_plural': 'Users'}, + ), + migrations.AlterModelOptions( + name='locationhistory', + options={'verbose_name': 'Location History', 'verbose_name_plural': 'Location History'}, + ), + migrations.AlterModelOptions( + name='loginhistory', + options={'verbose_name': 'Login History', 'verbose_name_plural': 'Login Histories'}, + ), + migrations.AlterModelOptions( + name='notification', + options={'verbose_name': 'Notification', 'verbose_name_plural': 'Notifications'}, + ), + migrations.AlterModelOptions( + name='user', + options={'ordering': ('-id',), 'verbose_name': 'User', 'verbose_name_plural': 'Users'}, + ), + migrations.AlterField( + model_name='locationhistory', + name='at_time', + field=models.DateTimeField(auto_now_add=True, verbose_name='at time'), + ), + migrations.AlterField( + model_name='locationhistory', + name='ip', + field=models.CharField(blank=True, max_length=255, null=True, verbose_name='ip'), + ), + migrations.AlterField( + model_name='locationhistory', + name='selected_manually', + field=models.BooleanField(blank=True, null=True, verbose_name='selected manually'), + ), + migrations.AlterField( + model_name='locationhistory', + name='timezone', + field=models.CharField(blank=True, max_length=60, null=True, verbose_name='timezone'), + ), + migrations.AlterField( + model_name='locationhistory', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='location_history', to=settings.AUTH_USER_MODEL, verbose_name='user'), + ), + migrations.AlterField( + model_name='loginhistory', + name='at_time', + field=models.DateTimeField(auto_now_add=True, verbose_name='at time'), + ), + migrations.AlterField( + model_name='loginhistory', + name='device_os', + field=models.CharField(blank=True, max_length=16, null=True, verbose_name='Device os'), + ), + migrations.AlterField( + model_name='loginhistory', + name='ip', + field=models.CharField(max_length=255, null=True, verbose_name='ip'), + ), + migrations.AlterField( + model_name='loginhistory', + name='timezone', + field=models.CharField(blank=True, max_length=100, null=True, verbose_name='timezone'), + ), + migrations.AlterField( + model_name='loginhistory', + name='user', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='login_history', to=settings.AUTH_USER_MODEL, verbose_name='user'), + ), + migrations.AlterField( + model_name='user', + name='avatar', + field=models.ImageField(blank=True, max_length=512, null=True, upload_to='users/avatars/%Y/%m/', verbose_name='Avatar'), + ), + migrations.AlterField( + model_name='user', + name='deleted_at', + field=models.DateTimeField(blank=True, null=True, verbose_name='deleted at'), + ), + migrations.AlterField( + model_name='user', + name='device_os', + field=models.CharField(choices=[('android', 'android'), ('apple', 'apple'), ('web', 'web')], max_length=16, null=True, verbose_name='device os'), + ), + migrations.AlterField( + model_name='user', + name='fcm', + field=models.CharField(blank=True, max_length=512, null=True, verbose_name='fcm'), + ), + migrations.AlterField( + model_name='user', + name='is_staff', + field=models.BooleanField(default=False, verbose_name='is staff'), + ), + migrations.AlterField( + model_name='user', + name='language', + field=dj_language.field.LanguageField(default=69, limit_choices_to={'status': True}, null=True, on_delete=django.db.models.deletion.PROTECT, related_name='+', to='dj_language.language', verbose_name='Language'), + ), + migrations.AlterField( + model_name='user', + name='skill', + field=models.CharField(blank=True, max_length=512, null=True, verbose_name='skill'), + ), + migrations.AlterField( + model_name='user', + name='slug', + field=models.SlugField(blank=True, max_length=255, null=True, unique=True, verbose_name='slug'), + ), + ] diff --git a/apps/account/models/groups.py b/apps/account/models/groups.py index 3831644..8cb926d 100644 --- a/apps/account/models/groups.py +++ b/apps/account/models/groups.py @@ -1,95 +1,96 @@ -from apps.account.models import User -from apps.account.manager import * - -from django.contrib.auth.models import Group - - - - - -class ProfessorUser(User): - objects = ProfessorUserManager() - - def save(self, *args, **kwargs): - self.user_type = User.UserType.PROFESSOR - super().save(*args, **kwargs) - - group, _ = Group.objects.get_or_create(name="Professor Group") - self.groups.add(group) - - class Meta: - proxy = True - verbose_name = "Professor User" - verbose_name_plural = "Professor Users" - - - - -class ClientUser(User): - objects = ClientUserManager() - - def save(self, *args, **kwargs): - self.user_type = User.UserType.CLIENT - super().save(*args, **kwargs) - - group, _ = Group.objects.get_or_create(name="Client Group") - self.groups.add(group) - - - class Meta: - proxy = True - - verbose_name = 'user' - verbose_name_plural = 'users' - ordering = ('-id',) - - - -class AdminUser(User): - objects = AdminUserManager() - - def save(self, *args, **kwargs): - self.user_type = User.UserType.ADMIN - super().save(*args, **kwargs) - - group, _ = Group.objects.get_or_create(name="Admin Group") - self.groups.add(group) - - class Meta: - proxy = True - verbose_name = "Admin User" - verbose_name_plural = "Admin Users" - - - -class SuperAdminUser(User): - objects = SuperAdminUserManager() - - def save(self, *args, **kwargs): - self.user_type = User.UserType.SUPER_ADMIN - self.is_staff = True - super().save(*args, **kwargs) - - - - class Meta: - proxy = True - verbose_name = "Super Admin User" - verbose_name_plural = "Super Admin Users" - - - -class StudentUser(User): - objects = StudentUserManager() - - def save(self, *args, **kwargs): - self.user_type = User.UserType.STUDENT - super().save(*args, **kwargs) - - group, _ = Group.objects.get_or_create(name="Student Group") - self.groups.add(group) - - class Meta: - proxy = True - verbose_name = "Student User" - verbose_name_plural = "Student Users" \ No newline at end of file +from django.utils.translation import gettext_lazy as _ +from apps.account.models import User +from apps.account.manager import * + +from django.contrib.auth.models import Group + + + + + +class ProfessorUser(User): + objects = ProfessorUserManager() + + def save(self, *args, **kwargs): + self.user_type = User.UserType.PROFESSOR + super().save(*args, **kwargs) + + group, _ = Group.objects.get_or_create(name="Professor Group") + self.groups.add(group) + + class Meta: + proxy = True + verbose_name = _("Professor User") + verbose_name_plural = _("Professor Users") + + + + +class ClientUser(User): + objects = ClientUserManager() + + def save(self, *args, **kwargs): + self.user_type = User.UserType.CLIENT + super().save(*args, **kwargs) + + group, _ = Group.objects.get_or_create(name="Client Group") + self.groups.add(group) + + + class Meta: + proxy = True + + verbose_name = _('User') + verbose_name_plural = _('Users') + ordering = ('-id',) + + + +class AdminUser(User): + objects = AdminUserManager() + + def save(self, *args, **kwargs): + self.user_type = User.UserType.ADMIN + super().save(*args, **kwargs) + + group, _ = Group.objects.get_or_create(name="Admin Group") + self.groups.add(group) + + class Meta: + proxy = True + verbose_name = _("Admin User") + verbose_name_plural = _("Admin Users") + + + +class SuperAdminUser(User): + objects = SuperAdminUserManager() + + def save(self, *args, **kwargs): + self.user_type = User.UserType.SUPER_ADMIN + self.is_staff = True + super().save(*args, **kwargs) + + + + class Meta: + proxy = True + verbose_name = _("Super Admin User") + verbose_name_plural = _("Super Admin Users") + + + +class StudentUser(User): + objects = StudentUserManager() + + def save(self, *args, **kwargs): + self.user_type = User.UserType.STUDENT + super().save(*args, **kwargs) + + group, _ = Group.objects.get_or_create(name="Student Group") + self.groups.add(group) + + class Meta: + proxy = True + verbose_name = _("Student User") + verbose_name_plural = _("Student Users") \ No newline at end of file diff --git a/apps/account/models/notification.py b/apps/account/models/notification.py index 8286fed..d9394a7 100644 --- a/apps/account/models/notification.py +++ b/apps/account/models/notification.py @@ -1,11 +1,11 @@ from django.db import models -from django.utils.translation import gettext as _ +from django.utils.translation import gettext_lazy as _ class Notification(models.Model): class ServiceChoices(models.TextChoices): - IMAM_JAVAD = 'imam-javad', 'Imam Javad' - DOBOODI = 'doboodi', 'Doboodi' + IMAM_JAVAD = 'imam-javad', _('Imam Javad') + DOBOODI = 'doboodi', _('Doboodi') title = models.CharField(max_length=255, verbose_name=_('title')) message = models.TextField(max_length=512, verbose_name=_('message')) @@ -20,6 +20,11 @@ class Notification(models.Model): created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('created at'), null=True) updated_at = models.DateTimeField(auto_now=True, verbose_name=_('updated at'), null=True) + class Meta: + verbose_name = _('Notification') + verbose_name_plural = _('Notifications') + def __str__(self): return self.title + diff --git a/apps/account/models/user.py b/apps/account/models/user.py index 89e8f57..b565d72 100644 --- a/apps/account/models/user.py +++ b/apps/account/models/user.py @@ -14,65 +14,65 @@ from apps.account.manager import UserManager class User(AbstractUser): class DeviceOs(models.TextChoices): - android = 'android', 'android' - apple = 'apple', 'apple' - web = 'web', 'web' + android = 'android', _('android') + apple = 'apple', _('apple') + web = 'web', _('web') class UserType(models.TextChoices): - PROFESSOR = 'professor', 'Professor' - CLIENT = 'client', 'Client' - STUDENT = 'student', "Student" - ADMIN = 'admin', 'Admin' - SUPER_ADMIN = 'super_admin', 'Super Admin' + PROFESSOR = 'professor', _('Professor') + CLIENT = 'client', _('Client') + STUDENT = 'student', _("Student") + ADMIN = 'admin', _('Admin') + SUPER_ADMIN = 'super_admin', _('Super Admin') class GenderChoices(models.TextChoices): - MALE = 'male', 'Male' - FEMALE = 'female', 'Female' + MALE = 'male', _('Male') + FEMALE = 'female', _('Female') last_name = None first_name = None username = models.CharField(unique=True, null=True, blank=True, max_length=150, - verbose_name=_("Username"), - error_messages={'unique': _("A user with that username already exists.")}) + verbose_name=_("Username"), + error_messages={'unique': _("A user with that username already exists.")}) email = models.EmailField(unique=True, verbose_name=_("Email Address"), - help_text=_("Enter the user's email address."), - null=True, blank=True, - error_messages={'unique': _("A user with that email already exists.")}) + help_text=_("Enter the user's email address."), + null=True, blank=True, + error_messages={'unique': _("A user with that email already exists.")}) fullname = models.CharField(max_length=255, verbose_name=_("Full Name"), - help_text=_("Enter the full name of the user."), - null=True, blank=True) + help_text=_("Enter the full name of the user."), + null=True, blank=True) birthdate = models.DateField(verbose_name=_('birthdate'), null=True, blank=True) - avatar = models.ImageField(max_length=512, null=True, blank=True, upload_to='users/avatars/%Y/%m/') + avatar = models.ImageField(max_length=512, null=True, blank=True, upload_to='users/avatars/%Y/%m/', verbose_name=_('Avatar')) phone_number = PhoneNumberField( validators=[validate_possible_number], null=True, blank=True, verbose_name=_('Phone Number'), - help_text="e.g., +49 151 12345678" + help_text=_("e.g., +49 151 12345678") ) - language = LanguageField(null=True) + language = LanguageField(null=True, verbose_name=_('Language')) - gender = models.CharField(max_length=20, choices=GenderChoices.choices, null=True, blank=True, verbose_name=_('Gender'), help_text="Select the user's gender.") - user_type = models.CharField(max_length=20, choices=UserType.choices, default=UserType.CLIENT, verbose_name="User Type", help_text="Type of the user.") - date_joined = models.DateTimeField(auto_now_add=True, verbose_name="Date Joined", help_text="The date and time the user registered.") + gender = models.CharField(max_length=20, choices=GenderChoices.choices, null=True, blank=True, verbose_name=_('Gender'), help_text=_("Select the user's gender.")) + user_type = models.CharField(max_length=20, choices=UserType.choices, default=UserType.CLIENT, verbose_name=_("User Type"), help_text=_("Type of the user.")) + date_joined = models.DateTimeField(auto_now_add=True, verbose_name=_("Date Joined"), help_text=_("The date and time the user registered.")) city = models.CharField(verbose_name=_('City'), max_length=255, null=True, blank=True) country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True) device_id = models.CharField(verbose_name=_('device id'), max_length=255, null=True, blank=True) - device_os = models.CharField(choices=DeviceOs.choices, null=True, max_length=16) + device_os = models.CharField(choices=DeviceOs.choices, null=True, max_length=16, verbose_name=_('device os')) user_agent = models.TextField(verbose_name=_('user agent'), null=True, blank=True) client_ip = models.TextField(verbose_name=_('client ip'), null=True, blank=True) - fcm = models.CharField(max_length=512, null=True, blank=True) - slug = models.SlugField(max_length=255, unique=True, null=True, blank=True) + fcm = models.CharField(max_length=512, null=True, blank=True, verbose_name=_('fcm')) + slug = models.SlugField(max_length=255, unique=True, null=True, blank=True, verbose_name=_('slug')) experience_years = models.PositiveIntegerField(default=0, verbose_name=_('Experience years')) - is_staff = models.BooleanField(default=False) - is_active = models.BooleanField(default=True, verbose_name="Active", help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.") - deleted_at = models.DateTimeField(null=True, blank=True) - info = models.TextField(verbose_name="Info", null=True, blank=True) - skill = models.CharField(max_length=512, null=True, blank=True) + is_staff = models.BooleanField(default=False, verbose_name=_('is staff')) + is_active = models.BooleanField(default=True, verbose_name=_("Active"), help_text=_("Designates whether this user should be treated as active. Unselect this instead of deleting accounts.")) + deleted_at = models.DateTimeField(null=True, blank=True, verbose_name=_('deleted at')) + info = models.TextField(verbose_name=_("Info"), null=True, blank=True) + skill = models.CharField(max_length=512, null=True, blank=True, verbose_name=_('skill')) objects = UserManager() @@ -286,8 +286,8 @@ class User(AbstractUser): class Meta: ordering = ("-id",) - verbose_name = "All Users" - verbose_name_plural = "All Users" + verbose_name = _("User") + verbose_name_plural = _("Users") unique_together = ( 'email', ) @@ -295,26 +295,35 @@ class User(AbstractUser): class LoginHistory(models.Model): - user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='login_history') + user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='login_history', verbose_name=_('user')) lat = models.FloatField(verbose_name=_('lat'), null=True, blank=True) lon = models.FloatField(verbose_name=_('lon'), null=True, blank=True) country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True) city = models.CharField(max_length=255, verbose_name=_('city'), null=True, blank=True) - ip = models.CharField(max_length=255, null=True) - timezone = models.CharField(max_length=100, null=True, blank=True) + ip = models.CharField(max_length=255, null=True, verbose_name=_('ip')) + timezone = models.CharField(max_length=100, null=True, blank=True, verbose_name=_('timezone')) user_agent = models.TextField(verbose_name=_('user agent'), null=True, blank=True) - device_os = models.CharField(max_length=16, null=True, blank=True) - at_time = models.DateTimeField(auto_now_add=True) + device_os = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Device os')) + at_time = models.DateTimeField(auto_now_add=True, verbose_name=_('at time')) + + class Meta: + verbose_name = _('Login History') + verbose_name_plural = _('Login Histories') class LocationHistory(models.Model): - user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='location_history') + user = models.ForeignKey("account.User", on_delete=models.CASCADE, related_name='location_history', verbose_name=_('user')) lat = models.FloatField(verbose_name=_('lat')) lon = models.FloatField(verbose_name=_('lon')) country = models.CharField(max_length=255, verbose_name=_('country'), null=True, blank=True) city = models.CharField(max_length=255, verbose_name=_('city'), null=True, blank=True) - selected_manually = models.BooleanField(null=True, blank=True) - ip = models.CharField(max_length=255, null=True, blank=True) - timezone = models.CharField(null=True, blank=True, max_length=60) - at_time = models.DateTimeField(auto_now_add=True) + selected_manually = models.BooleanField(null=True, blank=True, verbose_name=_('selected manually')) + ip = models.CharField(max_length=255, null=True, blank=True, verbose_name=_('ip')) + timezone = models.CharField(null=True, blank=True, max_length=60, verbose_name=_('timezone')) + at_time = models.DateTimeField(auto_now_add=True, verbose_name=_('at time')) + + class Meta: + verbose_name = _('Location History') + verbose_name_plural = _('Location History') + diff --git a/apps/certificate/migrations/0002_alter_certificate_course_and_more.py b/apps/certificate/migrations/0002_alter_certificate_course_and_more.py new file mode 100644 index 0000000..ba8c885 --- /dev/null +++ b/apps/certificate/migrations/0002_alter_certificate_course_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 5.2.12 on 2026-05-04 12:53 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0003_alter_clientuser_options_and_more'), + ('certificate', '0001_initial'), + ('course', '0006_alter_course_professor_alter_course_video_file_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='certificate', + name='course', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='course_certificates', to='course.course', verbose_name='Course'), + ), + migrations.AlterField( + model_name='certificate', + name='created_at', + field=models.DateTimeField(auto_now_add=True, verbose_name='Created at'), + ), + migrations.AlterField( + model_name='certificate', + name='status', + field=models.CharField(choices=[('pending', 'pending'), ('approved', 'approved'), ('canceled', 'canceled')], default='pending', max_length=10, verbose_name='Status'), + ), + migrations.AlterField( + model_name='certificate', + name='student', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='certificates', to='account.studentuser', verbose_name='Student'), + ), + ] diff --git a/apps/certificate/models.py b/apps/certificate/models.py index 835f4ed..9a321ee 100644 --- a/apps/certificate/models.py +++ b/apps/certificate/models.py @@ -14,12 +14,12 @@ class Certificate(models.Model): ('canceled', _('canceled')), ] - student = models.ForeignKey(StudentUser, on_delete=models.CASCADE, related_name='certificates') - course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='course_certificates') - status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending') + student = models.ForeignKey(StudentUser, on_delete=models.CASCADE, related_name='certificates', verbose_name=_('Student')) + course = models.ForeignKey(Course, on_delete=models.CASCADE, related_name='course_certificates', verbose_name=_('Course')) + status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='pending', verbose_name=_('Status')) certificate_file = models.FileField(upload_to='certificates/', null=True, blank=True, verbose_name=_('certificate_file')) - created_at = models.DateTimeField(auto_now_add=True) + created_at = models.DateTimeField(auto_now_add=True, verbose_name=_('Created at')) updated_at = models.DateTimeField(auto_now=True) def __str__(self): diff --git a/apps/course/admin/live_session.py b/apps/course/admin/live_session.py index 01ff51d..9c571fd 100644 --- a/apps/course/admin/live_session.py +++ b/apps/course/admin/live_session.py @@ -22,6 +22,15 @@ from django.contrib.auth import get_user_model User = get_user_model() +# 🔔 CUSTOM FILTER: Only show active, non-guest users in the right sidebar filter +class ActiveUserDropdownFilter(MultipleRelatedDropdownFilter): + def field_choices(self, field, request, model_admin): + return field.get_choices( + include_blank=False, + limit_choices_to={'is_active': True, 'email__isnull': False} + ) + + # --- WIDTH ENFORCEMENT & PLACEHOLDER TEXT FOR DROPDOWNS --- class MinWidthInlineForm(forms.ModelForm): def __init__(self, *args, **kwargs): @@ -62,7 +71,7 @@ class LiveSessionUserInline(StackedInline): verbose_name = _("Session User") verbose_name_plural = _("Session Users") - # 🔔 FILTER THE USER DROPDOWN TO ONLY SHOW ACTIVE & NON-GUEST USERS + # 🔔 FILTER THE USER DROPDOWN IN THE FORM ITSELF def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "user": kwargs["queryset"] = User.objects.filter(is_active=True, email__isnull=False) @@ -113,7 +122,7 @@ class LiveSessionUserAdmin(ModelAdmin): list_display = ("user", "session", "role", "is_online", "entered_at", "exited_at") list_filter = ( ("session", MultipleRelatedDropdownFilter), - ("user", MultipleRelatedDropdownFilter), + ("user", ActiveUserDropdownFilter), # 🔔 APPLIED CUSTOM FILTER HERE! ("role", ChoicesDropdownFilter), ("entered_at", RangeDateFilter), ("is_online", admin.BooleanFieldListFilter), @@ -134,7 +143,7 @@ class LiveSessionUserAdmin(ModelAdmin): def get_role_choices(self, request): return USER_ROLE_CHOICES - # 🔔 FILTER THE STANDALONE ADMIN FORM AS WELL JUST IN CASE + # FILTER THE STANDALONE ADMIN FORM AS WELL JUST IN CASE def formfield_for_foreignkey(self, db_field, request, **kwargs): if db_field.name == "user": kwargs["queryset"] = User.objects.filter(is_active=True, email__isnull=False) diff --git a/apps/course/migrations/0006_alter_course_professor_alter_course_video_file_and_more.py b/apps/course/migrations/0006_alter_course_professor_alter_course_video_file_and_more.py new file mode 100644 index 0000000..84fe8a8 --- /dev/null +++ b/apps/course/migrations/0006_alter_course_professor_alter_course_video_file_and_more.py @@ -0,0 +1,31 @@ +# Generated by Django 5.2.12 on 2026-05-04 12:53 + +import apps.course.models.course +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('account', '0003_alter_clientuser_options_and_more'), + ('course', '0005_alter_course_discount_percentage'), + ] + + operations = [ + migrations.AlterField( + model_name='course', + name='professor', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='courses', to='account.professoruser', verbose_name='Professor'), + ), + migrations.AlterField( + model_name='course', + name='video_file', + field=models.FileField(blank=True, null=True, upload_to=apps.course.models.course.course_file_upload_to, verbose_name='Video File'), + ), + migrations.AlterField( + model_name='course', + name='video_link', + field=models.CharField(blank=True, max_length=500, null=True, verbose_name='Video Link'), + ), + ] diff --git a/apps/course/models/course.py b/apps/course/models/course.py index 86a6ed2..227c483 100644 --- a/apps/course/models/course.py +++ b/apps/course/models/course.py @@ -69,7 +69,8 @@ class Course(models.Model): professor = models.ForeignKey( ProfessorUser, on_delete=models.CASCADE, - related_name="courses" + related_name="courses", + verbose_name=_("Professor") ) thumbnail = models.ImageField(upload_to="courses/thumbnails/", verbose_name=_('Thumbnail')) @@ -81,9 +82,10 @@ class Course(models.Model): video_file = models.FileField( upload_to=course_file_upload_to, null=True, - blank=True + blank=True, + verbose_name=_("Video File") ) - video_link = models.CharField(max_length=500, null=True, blank=True) + video_link = models.CharField(max_length=500, null=True, blank=True, verbose_name=_("Video Link")) is_online = models.BooleanField(default=False, verbose_name=_('Is Online Course')) online_link = models.CharField(max_length=500, null=True, blank=True, verbose_name=_('Online Class Link')) diff --git a/locale/ru/LC_MESSAGES/django.po b/locale/ru/LC_MESSAGES/django.po index a9f3a04..69068a2 100644 --- a/locale/ru/LC_MESSAGES/django.po +++ b/locale/ru/LC_MESSAGES/django.po @@ -9,7 +9,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2026-05-03 16:36+0330\n" +"POT-Creation-Date: 2026-05-04 12:19+0330\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -22,6 +22,7 @@ msgstr "" "(n%100>=11 && n%100<=14)? 2 : 3);\n" #: .\apps\account\admin\location.py:21 .\apps\account\admin\location.py:22 +#: .\apps\account\models\user.py:326 .\apps\account\models\user.py:327 msgid "Location History" msgstr "История местоположений" @@ -40,14 +41,29 @@ msgstr "Местоположение" msgid "Date & Time" msgstr "Дата и время" +#: .\apps\account\admin\professor.py:22 +msgid "Enter the phone number in international format. Example: +989012023212" +msgstr "Введите номер телефона в международном формате. Пример: +989012023212" + +#: .\apps\account\admin\professor.py:45 .\apps\account\admin\student.py:32 +#, fuzzy +#| msgid "Other" +msgid "other" +msgstr "Другой" + +#: .\apps\account\admin\professor.py:49 .\apps\account\admin\student.py:36 +#: .\apps\account\admin\user.py:87 +msgid "Password" +msgstr "Пароль" + #: .\apps\account\admin\professor.py:59 .\apps\account\admin\student.py:45 msgid "Personal info" msgstr "Личная информация" #: .\apps\account\admin\professor.py:60 .\apps\account\admin\student.py:46 #: .\apps\account\admin\user.py:91 .\apps\account\admin\user.py:115 -#: .\apps\account\admin\user.py:138 .\apps\account\admin\user.py:401 -#: .\apps\account\admin\user.py:463 .\apps\account\admin\user.py:466 +#: .\apps\account\admin\user.py:138 .\apps\account\admin\user.py:402 +#: .\apps\account\admin\user.py:465 .\apps\account\admin\user.py:468 #: .\templates\admin\filer\folder\directory_table.html:129 msgid "Permissions" msgstr "Права доступа" @@ -57,18 +73,37 @@ msgstr "Права доступа" msgid "Important dates" msgstr "Важные даты" +#: .\apps\account\admin\professor.py:75 +#, fuzzy, python-brace-format +#| msgid "A user with this email already exists." +msgid "A professor with the email {email} already exists." +msgstr "Пользователь с таким email уже существует." + +#: .\apps\account\admin\professor.py:96 +#, python-brace-format +msgid "The user with email {email} has been converted to a professor." +msgstr "Пользователь с email {email} был преобразован в профессора." + +#: .\apps\account\admin\professor.py:109 .\apps\account\admin\student.py:51 +#: .\apps\account\models\user.py:51 +msgid "Phone Number" +msgstr "Номер телефона" + #: .\apps\account\admin\student.py:55 msgid "Enrolled Courses" msgstr "Записанные курсы" + +#: .\apps\account\admin\student.py:59 +#, fuzzy, python-brace-format +#| msgid "courses" +msgid "{count} courses" +msgstr "{count} курсов" + #: .\apps\account\admin\user.py:47 msgid "A user with this email already exists." msgstr "Пользователь с таким email уже существует." -#: .\apps\account\admin\user.py:87 -msgid "Password" -msgstr "Пароль" - #: .\apps\account\admin\user.py:99 .\apps\account\admin\user.py:143 #: .\apps\blog\admin.py:67 .\apps\blog\admin.py:109 msgid "Basic Information" @@ -87,6 +122,7 @@ msgid "Authentication" msgstr "Аутентификация" #: .\apps\account\admin\user.py:159 .\apps\account\admin\user.py:196 +#: .\apps\account\models\user.py:58 msgid "Date Joined" msgstr "Дата регистрации" @@ -106,13 +142,14 @@ msgstr "Участие в курсе" msgid "Course Participations" msgstr "Участия в курсах" -#: .\apps\account\admin\user.py:216 .\apps\course\models\course.py:95 +#: .\apps\account\admin\user.py:216 .\apps\course\models\course.py:98 #: .\templates\course\course_stats.html:106 msgid "Course Status" msgstr "Статус курса" -#: .\apps\account\admin\user.py:222 .\apps\account\admin\user.py:407 -#: .\apps\course\admin\course.py:287 +#: .\apps\account\admin\user.py:222 .\apps\account\admin\user.py:408 +#: .\apps\account\models\user.py:22 .\apps\course\admin\course.py:287 +#: .\apps\course\models\course.py:73 msgid "Professor" msgstr "Профессор" @@ -120,7 +157,8 @@ msgstr "Профессор" msgid "password" msgstr "пароль" -#: .\apps\account\admin\user.py:257 .\apps\course\admin\course.py:485 +#: .\apps\account\admin\user.py:257 .\apps\account\models\user.py:24 +#: .\apps\certificate\models.py:17 .\apps\course\admin\course.py:485 #: .\apps\course\models\lesson.py:125 .\apps\course\models\participant.py:13 #: .\templates\course\course_analytics.html:186 msgid "Student" @@ -130,35 +168,44 @@ msgstr "Студент" msgid "Age" msgstr "Возраст" -#: .\apps\account\admin\user.py:283 .\apps\account\admin\user.py:423 -#: .\apps\course\admin\course.py:64 .\apps\course\models\course.py:154 +#: .\apps\account\admin\user.py:281 +#, python-brace-format +msgid "Born on {date}" +msgstr "Дата рождения: {date}" + +#: .\apps\account\admin\user.py:283 .\apps\account\admin\user.py:424 +#: .\apps\course\admin\course.py:64 .\apps\course\models\course.py:157 #: .\config\settings\base.py:480 .\config\settings\base.py:630 #: .\config\settings\base.py:642 msgid "Courses" msgstr "Курсы" -#: .\apps\account\admin\user.py:307 .\apps\account\admin\user.py:446 -msgid "courses" -msgstr "курсы" +#: .\apps\account\admin\user.py:307 .\apps\account\admin\user.py:447 +#, fuzzy, python-brace-format +#| msgid "Total Score" +msgid "{total} courses" +msgstr "Общий балл" -#: .\apps\account\admin\user.py:320 .\apps\course\admin\course.py:35 +#: .\apps\account\admin\user.py:321 .\apps\course\admin\course.py:35 msgid "Course Categories" msgstr "Категории курсов" -#: .\apps\account\admin\user.py:332 .\apps\course\admin\course.py:51 +#: .\apps\account\admin\user.py:333 .\apps\course\admin\course.py:51 msgid "Edit" msgstr "Редактировать" -#: .\apps\account\admin\user.py:338 +#: .\apps\account\admin\user.py:339 msgid "Select Existing User" msgstr "Выбрать существующего пользователя" -#: .\apps\account\admin\user.py:339 +#: .\apps\account\admin\user.py:340 msgid "Choose an existing user to upgrade to Professor." msgstr "Выберите существующего пользователя, чтобы назначить его профессором." -#: .\apps\account\admin\user.py:469 -msgid "permissions" +#: .\apps\account\admin\user.py:471 +#, fuzzy, python-brace-format +#| msgid "permissions" +msgid "{count} permissions" msgstr "права доступа" #: .\apps\account\middleware\admin_access.py:107 @@ -169,6 +216,77 @@ msgstr "У вас ограниченный доступ как у професс msgid "You do not have permission to access this page." msgstr "У вас нет прав для доступа к этой странице." +#: .\apps\account\models\groups.py:23 +#, fuzzy +#| msgid "Professors" +msgid "Professor User" +msgstr "Профессора" + +#: .\apps\account\models\groups.py:24 +#, fuzzy +#| msgid "Professors" +msgid "Professor Users" +msgstr "Профессора" + +#: .\apps\account\models\groups.py:43 .\apps\account\models\user.py:289 +#: .\apps\chat\models.py:166 .\apps\course\models\live_session.py:83 +#: .\apps\quiz\models\participant.py:11 .\apps\transaction\models.py:30 +msgid "User" +msgstr "Пользователь" + +#: .\apps\account\models\groups.py:44 .\apps\account\models\user.py:290 +#: .\config\settings\base.py:444 +msgid "Users" +msgstr "Пользователи" + +#: .\apps\account\models\groups.py:61 +#, fuzzy +#| msgid "Session User" +msgid "Admin User" +msgstr "Пользователь сессии" + +#: .\apps\account\models\groups.py:62 +#, fuzzy +#| msgid "All Users" +msgid "Admin Users" +msgstr "Все пользователи" + +#: .\apps\account\models\groups.py:78 +#, fuzzy +#| msgid "Session User" +msgid "Super Admin User" +msgstr "Пользователь сессии" + +#: .\apps\account\models\groups.py:79 +#, fuzzy +#| msgid "Session Users" +msgid "Super Admin Users" +msgstr "Пользователи сессии" + +#: .\apps\account\models\groups.py:95 +#, fuzzy +#| msgid "Students" +msgid "Student User" +msgstr "Студенты" + +#: .\apps\account\models\groups.py:96 +#, fuzzy +#| msgid "Students" +msgid "Student Users" +msgstr "Студенты" + +#: .\apps\account\models\notification.py:7 +#, fuzzy +#| msgid "Imam Javad Site" +msgid "Imam Javad" +msgstr "Сайт Имам Джавад" + +#: .\apps\account\models\notification.py:8 +#, fuzzy +#| msgid "Dovoodi Site" +msgid "Doboodi" +msgstr "Сайт Довуди" + #: .\apps\account\models\notification.py:10 .\apps\article\models.py:9 #: .\apps\article\models.py:184 .\apps\dobodbi_calendar\models.py:20 #: .\apps\podcast\models.py:9 .\apps\podcast\models.py:128 @@ -180,7 +298,8 @@ msgstr "название" msgid "message" msgstr "сообщение" -#: .\apps\account\models\notification.py:12 .\apps\library\models.py:171 +#: .\apps\account\models\notification.py:12 .\apps\account\models\user.py:298 +#: .\apps\account\models\user.py:315 .\apps\library\models.py:171 #: .\apps\podcast\models.py:255 msgid "user" msgstr "пользователь" @@ -240,6 +359,52 @@ msgstr "создано" msgid "updated at" msgstr "обновлено" +#: .\apps\account\models\notification.py:24 +#, fuzzy +#| msgid "Location" +msgid "Notification" +msgstr "Местоположение" + +#: .\apps\account\models\notification.py:25 +#, fuzzy +#| msgid "Location" +msgid "Notifications" +msgstr "Местоположение" + +#: .\apps\account\models\user.py:17 +msgid "android" +msgstr "" + +#: .\apps\account\models\user.py:18 +msgid "apple" +msgstr "" + +#: .\apps\account\models\user.py:19 +msgid "web" +msgstr "веб" + +#: .\apps\account\models\user.py:23 +#, fuzzy +#| msgid "client ip" +msgid "Client" +msgstr "ip клиента" + +#: .\apps\account\models\user.py:25 +msgid "Admin" +msgstr "Админ" + +#: .\apps\account\models\user.py:26 +msgid "Super Admin" +msgstr "Супер Админ" + +#: .\apps\account\models\user.py:29 .\apps\transaction\models.py:66 +msgid "Male" +msgstr "Мужской" + +#: .\apps\account\models\user.py:30 .\apps\transaction\models.py:67 +msgid "Female" +msgstr "Женский" + #: .\apps\account\models\user.py:35 msgid "Username" msgstr "Имя пользователя" @@ -273,20 +438,45 @@ msgstr "Введите полное имя пользователя." msgid "birthdate" msgstr "дата рождения" -#: .\apps\account\models\user.py:51 -msgid "Phone Number" -msgstr "Номер телефона" +#: .\apps\account\models\user.py:46 +msgid "Avatar" +msgstr "Аватар пользователя" + +#: .\apps\account\models\user.py:52 +msgid "e.g., +49 151 12345678" +msgstr "Например, +49 151 12345678" + +#: .\apps\account\models\user.py:54 .\apps\blog\models.py:199 +#: .\apps\hadis\models\reference.py:65 .\apps\library\models.py:119 +msgid "Language" +msgstr "Язык" #: .\apps\account\models\user.py:56 .\apps\transaction\models.py:79 msgid "Gender" msgstr "Пол" +#: .\apps\account\models\user.py:56 .\apps\transaction\models.py:79 +msgid "Select the user's gender." +msgstr "Выберите пол пользователя." + +#: .\apps\account\models\user.py:57 .\utils\unfold_translations.py:80 +msgid "User Type" +msgstr "Тип файла" + +#: .\apps\account\models\user.py:57 +msgid "Type of the user." +msgstr "Введите для поиска..." + +#: .\apps\account\models\user.py:58 +msgid "The date and time the user registered." +msgstr "Дата и время регистрации пользователя." + #: .\apps\account\models\user.py:60 msgid "City" msgstr "Город" #: .\apps\account\models\user.py:61 .\apps\account\models\user.py:301 -#: .\apps\account\models\user.py:314 +#: .\apps\account\models\user.py:318 msgid "country" msgstr "страна" @@ -294,6 +484,12 @@ msgstr "страна" msgid "device id" msgstr "id устройства" +#: .\apps\account\models\user.py:64 +#, fuzzy +#| msgid "Device os" +msgid "device os" +msgstr "id устройства" + #: .\apps\account\models\user.py:65 .\apps\account\models\user.py:305 msgid "user agent" msgstr "user agent" @@ -302,26 +498,109 @@ msgstr "user agent" msgid "client ip" msgstr "ip клиента" +#: .\apps\account\models\user.py:68 +msgid "fcm" +msgstr "" + +#: .\apps\account\models\user.py:69 .\apps\article\models.py:10 +#: .\apps\hadis\models\hadis.py:14 .\apps\hadis\models\hadis.py:170 +#: .\apps\hadis\models\hadis.py:265 .\apps\hadis\models\hadis.py:490 +#: .\apps\hadis\models\reference.py:70 .\apps\hadis\models\transmitter.py:22 +#: .\apps\hadis\models\transmitter.py:128 +#: .\apps\hadis\models\transmitter.py:237 +#: .\apps\hadis\models\transmitter.py:471 +#: .\apps\hadis\models\transmitter.py:637 .\apps\podcast\models.py:10 +#: .\apps\podcast\models.py:129 .\apps\video\models.py:12 +#: .\apps\video\models.py:145 +msgid "slug" +msgstr "ярлык (slug)" + #: .\apps\account\models\user.py:70 msgid "Experience years" msgstr "Годы опыта" +#: .\apps\account\models\user.py:71 +msgid "is staff" +msgstr "статус хадиса" + +#: .\apps\account\models\user.py:72 .\apps\api\models.py:111 +#: .\apps\chat\admin.py:315 .\apps\hadis\models\version.py:10 +#: .\apps\library\admin.py:212 .\apps\quiz\admin\quiz.py:68 +msgid "Active" +msgstr "Активно" + +#: .\apps\account\models\user.py:72 +msgid "" +"Designates whether this user should be treated as active. Unselect this " +"instead of deleting accounts." +msgstr "" +"Обозначает, должен ли этот пользователь считаться активным. Вместо удаления " +"учетных записей отключите это." + +#: .\apps\account\models\user.py:73 +#, fuzzy +#| msgid "Deleted At" +msgid "deleted at" +msgstr "Удалено в" + +#: .\apps\account\models\user.py:74 +msgid "Info" +msgstr "Инфо" + +#: .\apps\account\models\user.py:75 +msgid "skill" +msgstr "навык" + #: .\apps\account\models\user.py:118 msgid "Email is required for all regular users." msgstr "Email обязателен для всех обычных пользователей." -#: .\apps\account\models\user.py:299 .\apps\account\models\user.py:312 +#: .\apps\account\models\user.py:299 .\apps\account\models\user.py:316 msgid "lat" msgstr "широта" -#: .\apps\account\models\user.py:300 .\apps\account\models\user.py:313 +#: .\apps\account\models\user.py:300 .\apps\account\models\user.py:317 msgid "lon" msgstr "долгота" -#: .\apps\account\models\user.py:302 .\apps\account\models\user.py:315 +#: .\apps\account\models\user.py:302 .\apps\account\models\user.py:319 msgid "city" msgstr "город" +#: .\apps\account\models\user.py:303 .\apps\account\models\user.py:321 +msgid "ip" +msgstr "Ай Пи" + +#: .\apps\account\models\user.py:304 .\apps\account\models\user.py:322 +msgid "timezone" +msgstr "часовой пояс" + +#: .\apps\account\models\user.py:306 +msgid "Device os" +msgstr "id устройства" + +#: .\apps\account\models\user.py:307 .\apps\account\models\user.py:323 +#, fuzzy +#| msgid "total time" +msgid "at time" +msgstr "общее время" + +#: .\apps\account\models\user.py:310 +#, fuzzy +#| msgid "Location History" +msgid "Login History" +msgstr "История местоположений" + +#: .\apps\account\models\user.py:311 +#, fuzzy +#| msgid "Location History" +msgid "Login Histories" +msgstr "История местоположений" + +#: .\apps\account\models\user.py:320 +msgid "selected manually" +msgstr "выбрано вручную" + #: .\apps\account\templates\account\group_help_text.html:5 msgid "Driver before template" msgstr "Водитель до шаблона" @@ -343,18 +622,24 @@ msgid "Total races" msgstr "Всего гонок" #: .\apps\account\templates\account\user_list_section.html:8 -msgid "Total Actice Users" +#: .\utils\unfold_translations.py:83 +#, fuzzy +#| msgid "Total Actice Users" +msgid "Total Active Users" msgstr "Всего активных пользователей" #: .\apps\account\templates\account\user_list_section.html:16 +#: .\utils\unfold_translations.py:84 msgid "Total Guest Users" msgstr "Всего гостевых пользователей" #: .\apps\account\templates\account\user_list_section.html:22 +#: .\utils\unfold_translations.py:85 msgid "Total Students" msgstr "Всего студентов" #: .\apps\account\templates\account\user_list_section.html:28 +#: .\utils\unfold_translations.py:86 msgid "Total Professors" msgstr "Всего преподавателей" @@ -444,7 +729,7 @@ msgid "Application APK file" msgstr "APK файл приложения" #: .\apps\api\models.py:84 .\apps\blog\models.py:195 .\apps\chat\models.py:28 -#: .\apps\course\admin\course.py:397 .\apps\course\models\course.py:172 +#: .\apps\course\admin\course.py:397 .\apps\course\models\course.py:175 #: .\apps\hadis\admin\hadis.py:649 .\apps\hadis\models\category.py:16 #: .\apps\hadis\models\category.py:78 .\apps\hadis\models\hadis.py:173 #: .\apps\hadis\models\hadis.py:268 .\apps\hadis\models\hadis.py:426 @@ -484,21 +769,15 @@ msgstr "Загрузки из Google Play" msgid "Total number of downloads on Google Play" msgstr "Общее количество загрузок в Google Play" -#: .\apps\api\models.py:111 .\apps\chat\admin.py:315 -#: .\apps\hadis\models\version.py:10 .\apps\library\admin.py:212 -#: .\apps\quiz\admin\quiz.py:68 -msgid "Active" -msgstr "Активно" - #: .\apps\api\models.py:112 msgid "Is this version active?" msgstr "Эта версия активна?" #: .\apps\api\models.py:122 .\apps\blog\models.py:38 .\apps\blog\models.py:159 #: .\apps\chat\models.py:50 .\apps\chat\models.py:126 -#: .\apps\course\models\course.py:116 .\apps\course\models\course.py:174 -#: .\apps\course\models\course.py:192 .\apps\course\models\course.py:223 -#: .\apps\course\models\course.py:247 .\apps\course\models\lesson.py:36 +#: .\apps\course\models\course.py:119 .\apps\course\models\course.py:177 +#: .\apps\course\models\course.py:195 .\apps\course\models\course.py:226 +#: .\apps\course\models\course.py:250 .\apps\course\models\lesson.py:36 #: .\apps\course\models\lesson.py:62 .\apps\course\models\live_session.py:47 #: .\apps\course\models\live_session.py:109 #: .\apps\course\models\live_session.py:171 @@ -571,6 +850,7 @@ msgstr "Содержимое статей" #: .\apps\video\admin.py:83 .\utils\keyval_field.py:39 #: .\utils\keyval_field.py:59 .\utils\keyval_field.py:76 #: .\utils\keyval_field.py:139 .\utils\keyval_field.py:167 .\utils\schema.py:51 +#: .\utils\unfold_translations.py:74 msgid "Title" msgstr "Название" @@ -600,11 +880,11 @@ msgstr "Файл" #: .\apps\article\admin.py:210 .\apps\bookmark\admin.py:42 #: .\apps\bookmark\admin.py:112 .\apps\certificate\admin.py:30 -#: .\apps\chat\admin.py:257 .\apps\chat\admin.py:311 -#: .\apps\course\admin\live_session.py:158 .\apps\library\admin.py:68 -#: .\apps\library\admin.py:207 .\apps\podcast\admin.py:160 -#: .\apps\quiz\admin\quiz.py:65 .\apps\quiz\models\quiz.py:13 -#: .\apps\video\admin.py:169 +#: .\apps\certificate\models.py:19 .\apps\chat\admin.py:257 +#: .\apps\chat\admin.py:311 .\apps\course\admin\live_session.py:167 +#: .\apps\library\admin.py:68 .\apps\library\admin.py:207 +#: .\apps\podcast\admin.py:160 .\apps\quiz\admin\quiz.py:65 +#: .\apps\quiz\models\quiz.py:13 .\apps\video\admin.py:169 msgid "Status" msgstr "Статус" @@ -623,18 +903,6 @@ msgstr "Содержимое" msgid "Parts" msgstr "Части" -#: .\apps\article\models.py:10 .\apps\hadis\models\hadis.py:14 -#: .\apps\hadis\models\hadis.py:170 .\apps\hadis\models\hadis.py:265 -#: .\apps\hadis\models\hadis.py:490 .\apps\hadis\models\reference.py:70 -#: .\apps\hadis\models\transmitter.py:22 .\apps\hadis\models\transmitter.py:128 -#: .\apps\hadis\models\transmitter.py:237 -#: .\apps\hadis\models\transmitter.py:471 -#: .\apps\hadis\models\transmitter.py:637 .\apps\podcast\models.py:10 -#: .\apps\podcast\models.py:129 .\apps\video\models.py:12 -#: .\apps\video\models.py:145 -msgid "slug" -msgstr "ярлык (slug)" - #: .\apps\article\models.py:12 .\apps\article\models.py:42 #: .\apps\article\models.py:107 .\apps\article\models.py:188 #: .\apps\hadis\models\hadis.py:16 .\apps\hadis\models\hadis.py:142 @@ -804,9 +1072,9 @@ msgstr "Содержимое" #: .\apps\blog\admin.py:77 .\apps\blog\admin.py:115 .\apps\bookmark\admin.py:45 #: .\apps\bookmark\admin.py:115 .\apps\certificate\admin.py:24 #: .\apps\chat\admin.py:150 .\apps\chat\admin.py:261 -#: .\apps\course\admin\live_session.py:108 -#: .\apps\course\admin\live_session.py:131 -#: .\apps\course\admin\live_session.py:159 .\apps\hadis\admin\hadis.py:611 +#: .\apps\course\admin\live_session.py:117 +#: .\apps\course\admin\live_session.py:140 +#: .\apps\course\admin\live_session.py:168 .\apps\hadis\admin\hadis.py:611 #: .\apps\hadis\admin\hadis.py:687 .\apps\hadis\admin\hadis.py:719 #: .\apps\hadis\admin\hadis.py:762 .\apps\hadis\admin\hadis.py:814 #: .\apps\hadis\admin\hadis.py:943 .\apps\hadis\admin\reference.py:512 @@ -817,7 +1085,7 @@ msgstr "Содержимое" msgid "Timestamps" msgstr "Метки времени" -#: .\apps\blog\models.py:16 .\apps\course\models\course.py:74 +#: .\apps\blog\models.py:16 .\apps\course\models\course.py:76 #: .\apps\course\models\live_session.py:165 msgid "Thumbnail" msgstr "Миниатюра" @@ -924,11 +1192,6 @@ msgstr "" "описывает и резюмирует содержимое страницы для пользователей и поисковых " "систем" -#: .\apps\blog\models.py:199 .\apps\hadis\models\reference.py:65 -#: .\apps\library\models.py:119 -msgid "Language" -msgstr "Язык" - #: .\apps\blog\models.py:203 msgid "Blog SEO" msgstr "SEO блога" @@ -961,10 +1224,27 @@ msgstr "одобрено" msgid "canceled" msgstr "отменено" +#: .\apps\certificate\models.py:18 .\apps\chat\models.py:32 +#: .\apps\course\admin\course.py:271 .\apps\course\admin\course.py:503 +#: .\apps\course\models\course.py:156 .\apps\course\models\course.py:192 +#: .\apps\course\models\course.py:247 .\apps\course\models\lesson.py:55 +#: .\apps\course\models\live_session.py:13 +#: .\apps\course\models\participant.py:19 .\apps\transaction\models.py:31 +msgid "Course" +msgstr "Курс" + #: .\apps\certificate\models.py:20 msgid "certificate_file" msgstr "файл_сертификата" +#: .\apps\certificate\models.py:22 .\apps\course\models\course.py:118 +#: .\apps\course\models\course.py:176 .\apps\course\models\course.py:194 +#: .\apps\course\models\course.py:225 .\apps\course\models\course.py:249 +#: .\apps\course\models\lesson.py:35 .\apps\course\models\lesson.py:61 +#: .\apps\course\models\lesson.py:135 .\apps\transaction\models.py:36 +msgid "Created at" +msgstr "Дата создания" + #: .\apps\chat\admin.py:77 msgid "Recent Message" msgstr "Последнее сообщение" @@ -1027,7 +1307,7 @@ msgstr "Статусы прочтения" msgid "Message Information" msgstr "Информация о сообщении" -#: .\apps\chat\admin.py:249 .\apps\course\models\course.py:236 +#: .\apps\chat\admin.py:249 .\apps\course\models\course.py:239 msgid "Attachments" msgstr "Вложения" @@ -1044,6 +1324,7 @@ msgid "Content Preview" msgstr "Предпросмотр контента" #: .\apps\chat\admin.py:295 +#, python-format msgid "%(type)s content" msgstr "Содержимое (%(type)s)" @@ -1074,8 +1355,8 @@ msgstr "Размер" msgid "{} bytes" msgstr "{} байт" -#: .\apps\chat\admin.py:326 .\apps\course\models\course.py:235 -#: .\apps\course\models\course.py:245 +#: .\apps\chat\admin.py:326 .\apps\course\models\course.py:238 +#: .\apps\course\models\course.py:248 msgid "Attachment" msgstr "Вложение" @@ -1146,14 +1427,6 @@ msgstr "Очистка данных чата успешно завершена!" msgid "Room Name" msgstr "Название комнаты" -#: .\apps\chat\models.py:32 .\apps\course\admin\course.py:271 -#: .\apps\course\admin\course.py:503 .\apps\course\models\course.py:153 -#: .\apps\course\models\course.py:189 .\apps\course\models\course.py:244 -#: .\apps\course\models\lesson.py:55 .\apps\course\models\live_session.py:13 -#: .\apps\course\models\participant.py:19 .\apps\transaction\models.py:31 -msgid "Course" -msgstr "Курс" - #: .\apps\chat\models.py:37 .\utils\unfold_translations.py:29 msgid "Initiator" msgstr "Инициатор" @@ -1248,11 +1521,6 @@ msgstr "Сообщение чата" msgid "Chat Messages" msgstr "Сообщения чата" -#: .\apps\chat\models.py:166 .\apps\course\models\live_session.py:83 -#: .\apps\quiz\models\participant.py:11 .\apps\transaction\models.py:30 -msgid "User" -msgstr "Пользователь" - #: .\apps\chat\models.py:172 msgid "Message" msgstr "Сообщение" @@ -1289,15 +1557,15 @@ msgstr "Это поле обязательно для заполнения и н msgid "This field is required." msgstr "Это поле обязательно." -#: .\apps\course\admin\course.py:125 .\apps\course\admin\live_session.py:43 +#: .\apps\course\admin\course.py:125 .\apps\course\admin\live_session.py:52 msgid "Select a value" msgstr "Выберите значение" -#: .\apps\course\admin\course.py:135 .\apps\course\models\course.py:266 +#: .\apps\course\admin\course.py:135 .\apps\course\models\course.py:269 msgid "Course Attachment" msgstr "Вложение курса" -#: .\apps\course\admin\course.py:136 .\apps\course\models\course.py:267 +#: .\apps\course\admin\course.py:136 .\apps\course\models\course.py:270 #: .\config\settings\base.py:492 msgid "Course Attachments" msgstr "Вложения курсов" @@ -1318,7 +1586,7 @@ msgstr "Выберите студента" msgid "Settings & Status" msgstr "Настройки и статус" -#: .\apps\course\admin\course.py:251 .\apps\course\admin\live_session.py:82 +#: .\apps\course\admin\course.py:251 .\apps\course\admin\live_session.py:91 msgid "Media" msgstr "Медиа" @@ -1384,31 +1652,31 @@ msgstr "Продолжительность" msgid "min" msgstr "мин" -#: .\apps\course\admin\live_session.py:56 .\apps\course\models\course.py:113 +#: .\apps\course\admin\live_session.py:65 .\apps\course\models\course.py:116 msgid "Timing" msgstr "Расписание" -#: .\apps\course\admin\live_session.py:62 +#: .\apps\course\admin\live_session.py:71 msgid "Session User" msgstr "Пользователь сессии" -#: .\apps\course\admin\live_session.py:63 .\config\settings\base.py:521 +#: .\apps\course\admin\live_session.py:72 .\config\settings\base.py:521 msgid "Session Users" msgstr "Пользователи сессии" -#: .\apps\course\admin\live_session.py:88 +#: .\apps\course\admin\live_session.py:97 msgid "Session Recording" msgstr "Запись сессии" -#: .\apps\course\admin\live_session.py:89 .\config\settings\base.py:527 +#: .\apps\course\admin\live_session.py:98 .\config\settings\base.py:527 msgid "Session Recordings" msgstr "Записи сессий" -#: .\apps\course\admin\live_session.py:130 +#: .\apps\course\admin\live_session.py:139 msgid "Session Timing" msgstr "Время сессии" -#: .\apps\course\admin\live_session.py:157 .\apps\hadis\admin\category.py:198 +#: .\apps\course\admin\live_session.py:166 .\apps\hadis\admin\category.py:198 msgid "Files" msgstr "Файлы" @@ -1438,161 +1706,160 @@ msgstr ", " msgid "Course data clearing completed" msgstr "Очистка данных курсов завершена" -#: .\apps\course\models\course.py:29 +#: .\apps\course\models\course.py:30 msgid "Category Name" msgstr "Название категории" -#: .\apps\course\models\course.py:48 +#: .\apps\course\models\course.py:49 msgid "Beginner" msgstr "Начинающий" -#: .\apps\course\models\course.py:49 +#: .\apps\course\models\course.py:50 msgid "Mid Level" msgstr "Средний уровень" -#: .\apps\course\models\course.py:50 +#: .\apps\course\models\course.py:51 msgid "Advanced" msgstr "Продвинутый" -#: .\apps\course\models\course.py:53 .\apps\library\admin.py:216 +#: .\apps\course\models\course.py:54 .\apps\library\admin.py:216 #: .\apps\quiz\admin\quiz.py:69 msgid "Inactive" msgstr "Неактивно" -#: .\apps\course\models\course.py:54 +#: .\apps\course\models\course.py:55 msgid "Upcoming" msgstr "Предстоящий" -#: .\apps\course\models\course.py:55 +#: .\apps\course\models\course.py:56 msgid "Registering" msgstr "Идет регистрация" -#: .\apps\course\models\course.py:56 +#: .\apps\course\models\course.py:57 msgid "Ongoing" msgstr "Текущий" -#: .\apps\course\models\course.py:57 +#: .\apps\course\models\course.py:58 msgid "Finished" msgstr "Завершен" -#: .\apps\course\models\course.py:60 .\apps\course\models\lesson.py:21 +#: .\apps\course\models\course.py:61 .\apps\course\models\lesson.py:21 msgid "Youtube Link" msgstr "Ссылка на YouTube" -#: .\apps\course\models\course.py:61 .\apps\course\models\lesson.py:22 +#: .\apps\course\models\course.py:62 .\apps\course\models\course.py:86 +#: .\apps\course\models\lesson.py:22 msgid "Video File" msgstr "Видеофайл" -#: .\apps\course\models\course.py:65 +#: .\apps\course\models\course.py:66 msgid "Course Title" msgstr "Название курса" -#: .\apps\course\models\course.py:67 .\apps\library\models.py:92 +#: .\apps\course\models\course.py:68 .\apps\library\models.py:92 msgid "Category" msgstr "Категория" -#: .\apps\course\models\course.py:78 +#: .\apps\course\models\course.py:80 msgid "Preview Video Type (YouTube Link or File Upload)" msgstr "Тип превью-видео (ссылка на YouTube или загрузка файла)" -#: .\apps\course\models\course.py:87 +#: .\apps\course\models\course.py:88 .\utils\unfold_translations.py:71 +#, fuzzy +#| msgid "Video File" +msgid "Video Link" +msgstr "Видеофайл" + +#: .\apps\course\models\course.py:90 msgid "Is Online Course" msgstr "Это онлайн-курс" -#: .\apps\course\models\course.py:88 +#: .\apps\course\models\course.py:91 msgid "Online Class Link" msgstr "Ссылка на онлайн-занятие" -#: .\apps\course\models\course.py:89 +#: .\apps\course\models\course.py:92 msgid "Course Level" msgstr "Уровень курса" -#: .\apps\course\models\course.py:90 +#: .\apps\course\models\course.py:93 msgid "Duration (in hours)" msgstr "Продолжительность (в часах)" -#: .\apps\course\models\course.py:91 +#: .\apps\course\models\course.py:94 msgid "Number of Lessons" msgstr "Количество уроков" -#: .\apps\course\models\course.py:93 +#: .\apps\course\models\course.py:96 msgid "Course Description" msgstr "Описание курса" -#: .\apps\course\models\course.py:94 +#: .\apps\course\models\course.py:97 msgid "Short Description" msgstr "Краткое описание" -#: .\apps\course\models\course.py:96 +#: .\apps\course\models\course.py:99 msgid "Is Free" msgstr "Бесплатный" -#: .\apps\course\models\course.py:97 +#: .\apps\course\models\course.py:100 msgid "Course Price" msgstr "Цена курса" -#: .\apps\course\models\course.py:98 +#: .\apps\course\models\course.py:101 msgid "Discount Percentage" msgstr "Процент скидки" -#: .\apps\course\models\course.py:100 +#: .\apps\course\models\course.py:103 msgid "Course Final Price" msgstr "Итоговая цена курса" -#: .\apps\course\models\course.py:101 +#: .\apps\course\models\course.py:104 msgid "" "This field is automatically calculated based on the discount percentage." msgstr "Это поле рассчитывается автоматически на основе процента скидки." -#: .\apps\course\models\course.py:106 +#: .\apps\course\models\course.py:109 msgid "Lock Group Chat" msgstr "Заблокировать групповой чат" -#: .\apps\course\models\course.py:110 +#: .\apps\course\models\course.py:113 msgid "Lock Private Chats with Professor" msgstr "Заблокировать личные чаты с профессором" -#: .\apps\course\models\course.py:114 +#: .\apps\course\models\course.py:117 msgid "Course features" msgstr "Особенности курса" -#: .\apps\course\models\course.py:115 .\apps\course\models\course.py:173 -#: .\apps\course\models\course.py:191 .\apps\course\models\course.py:222 -#: .\apps\course\models\course.py:246 .\apps\course\models\lesson.py:35 -#: .\apps\course\models\lesson.py:61 .\apps\course\models\lesson.py:135 -#: .\apps\transaction\models.py:36 -msgid "Created at" -msgstr "Дата создания" - -#: .\apps\course\models\course.py:171 +#: .\apps\course\models\course.py:174 msgid "Glossary Title" msgstr "Название глоссария" -#: .\apps\course\models\course.py:180 .\apps\course\models\course.py:190 +#: .\apps\course\models\course.py:183 .\apps\course\models\course.py:193 msgid "Glossary" msgstr "Глоссарий" -#: .\apps\course\models\course.py:181 +#: .\apps\course\models\course.py:184 msgid "Glossaries" msgstr "Глоссарии" -#: .\apps\course\models\course.py:207 .\config\settings\base.py:498 +#: .\apps\course\models\course.py:210 .\config\settings\base.py:498 msgid "Course Glossary" msgstr "Глоссарий курса" -#: .\apps\course\models\course.py:208 +#: .\apps\course\models\course.py:211 msgid "Course Glossaries" msgstr "Глоссарии курсов" -#: .\apps\course\models\course.py:216 +#: .\apps\course\models\course.py:219 msgid "Attachment Title" msgstr "Название вложения" -#: .\apps\course\models\course.py:219 +#: .\apps\course\models\course.py:222 msgid "Attachment File" msgstr "Файл вложения" -#: .\apps\course\models\course.py:221 +#: .\apps\course\models\course.py:224 msgid "File Size (in bytes)" msgstr "Размер файла (в байтах)" @@ -1839,6 +2106,7 @@ msgid "Joined Date" msgstr "Дата присоединения" #: .\apps\course\models\participant.py:28 .\apps\quiz\models\participant.py:22 +#: .\templates\admin\index.html:89 msgid "Participants" msgstr "Участники" @@ -2490,6 +2758,7 @@ msgid "Searching..." msgstr "Поиск..." #: .\apps\hadis\templates\admin\category_index.html:931 +#: .\utils\unfold_translations.py:66 msgid "Type to search..." msgstr "Введите для поиска..." @@ -2979,6 +3248,7 @@ msgid "Paid" msgstr "Оплачено" #: .\apps\transaction\admin.py:62 .\apps\transaction\models.py:23 +#: .\templates\admin\index.html:156 .\utils\unfold_translations.py:56 msgid "Failed" msgstr "Неуспешно" @@ -2987,6 +3257,7 @@ msgid "Waiting Approval" msgstr "Ожидает одобрения" #: .\apps\transaction\admin.py:65 .\apps\transaction\models.py:20 +#: .\templates\admin\index.html:146 .\utils\unfold_translations.py:54 msgid "Pending" msgstr "В ожидании" @@ -3041,7 +3312,8 @@ msgstr "" msgid "Waiting for Approval" msgstr "Ожидает одобрения" -#: .\apps\transaction\models.py:22 +#: .\apps\transaction\models.py:22 .\templates\admin\index.html:133 +#: .\templates\admin\index.html:141 .\utils\unfold_translations.py:53 msgid "Success" msgstr "Успешно" @@ -3061,7 +3333,7 @@ msgstr "Метод оплаты транзакции" msgid "Transaction Price" msgstr "Цена транзакции" -#: .\apps\transaction\models.py:35 +#: .\apps\transaction\models.py:35 .\templates\admin\index.html:103 msgid "Transaction Status" msgstr "Статус транзакции" @@ -3081,22 +3353,10 @@ msgstr "Участник транзакции" msgid "Transaction Participants" msgstr "Участники транзакции" -#: .\apps\transaction\models.py:66 -msgid "Male" -msgstr "Мужской" - -#: .\apps\transaction\models.py:67 -msgid "Female" -msgstr "Женский" - #: .\apps\transaction\models.py:77 msgid "phone" msgstr "телефон" -#: .\apps\transaction\models.py:79 -msgid "Select the user's gender." -msgstr "Выберите пол пользователя." - #: .\apps\transaction\models.py:87 msgid "Participant Info" msgstr "Информация об участнике" @@ -3253,10 +3513,6 @@ msgstr "Закрепленные коллекции" msgid "Regular Collections" msgstr "Обычные коллекции" -#: .\config\settings\base.py:444 -msgid "Users" -msgstr "Пользователи" - #: .\config\settings\base.py:452 msgid "Guest Users" msgstr "Гостевые пользователи" @@ -3274,7 +3530,7 @@ msgid "Quiz Participants" msgstr "Участники теста" #: .\config\settings\base.py:584 .\templates\admin\index.html:7 -#: .\utils\admin.py:462 +#: .\utils\admin.py:469 msgid "Dashboard" msgstr "Панель управления" @@ -3290,7 +3546,7 @@ msgstr "Все пользователи" msgid "Students" msgstr "Студенты" -#: .\config\settings\base.py:621 +#: .\config\settings\base.py:621 .\utils\admin.py:555 msgid "Professors" msgstr "Профессора" @@ -3502,10 +3758,28 @@ msgstr "Очистить выбор" msgid "System Overview" msgstr "Обзор системы" -#: .\templates\admin\index.html:42 +#: .\templates\admin\index.html:47 msgid "No statistics available for this panel." msgstr "Статистика для этой панели недоступна." +#: .\templates\admin\index.html:60 .\utils\unfold_translations.py:52 +msgid "Top 5 Popular Courses" +msgstr "Топ-5 популярных курсов" + +#: .\templates\admin\index.html:93 +msgid "No courses found." +msgstr "Курсы не найдены." + +#: .\templates\admin\index.html:151 .\utils\unfold_translations.py:55 +msgid "Waiting" +msgstr "В ожидании" + +#: .\templates\admin\index.html:164 +#, fuzzy +#| msgid "Transaction Receipt" +msgid "No transactions recorded yet." +msgstr "Квитанция транзакции" + #: .\templates\course\course_analytics.html:9 msgid "Course Analytics Dashboard" msgstr "Панель аналитики курса" @@ -3637,43 +3911,44 @@ msgstr "Разработка" msgid "Production" msgstr "Продакшн" -#: .\utils\admin.py:139 .\utils\admin.py:242 +#: .\utils\admin.py:139 .\utils\admin.py:251 msgid "Imam Javad Site" msgstr "Сайт Имам Джавад" -#: .\utils\admin.py:144 .\utils\admin.py:237 +#: .\utils\admin.py:144 .\utils\admin.py:246 msgid "Dovoodi Site" msgstr "Сайт Довуди" -#: .\utils\admin.py:149 -msgid "Dovoodi Admin" -msgstr "Админ Довуди" - -#: .\utils\admin.py:247 -msgid "Imam Javad Admin" -msgstr "Админ Имам Джавад" -#: .\utils\admin.py:489 +#: .\utils\admin.py:554 msgid "Active Students" msgstr "Активные студенты" -#: .\utils\admin.py:494 -msgid "Published Courses" -msgstr "Опубликованные курсы" +#: .\utils\admin.py:556 .\utils\unfold_translations.py:49 +msgid "Active Courses" +msgstr "Активные водители" + +#: .\utils\admin.py:557 +msgid "Total Blogs" +msgstr "Всего блогов" -#: .\utils\admin.py:499 +#: .\utils\admin.py:558 .\utils\unfold_translations.py:50 +msgid "30-Day Revenue" +msgstr "Доход за 30 дней" + +#: .\utils\admin.py:559 msgid "Pending Certificates" msgstr "Сертификаты в ожидании" -#: .\utils\admin.py:524 +#: .\utils\admin.py:579 msgid "Hadith Database" msgstr "База данных хадисов" -#: .\utils\admin.py:529 +#: .\utils\admin.py:580 msgid "Books & Articles" msgstr "Книги и статьи" -#: .\utils\admin.py:534 +#: .\utils\admin.py:581 msgid "Multimedia" msgstr "Мультимедиа" @@ -3759,7 +4034,7 @@ msgstr "Воскресенье" msgid "Day" msgstr "День" -#: .\utils\schema.py:36 +#: .\utils\schema.py:36 .\utils\unfold_translations.py:72 msgid "Time" msgstr "Время" @@ -3863,3 +4138,166 @@ msgstr "Название комнаты" #: .\utils\unfold_translations.py:39 msgid "System Administration" msgstr "Системное Администрирование" + +#: .\utils\unfold_translations.py:48 +msgid "Active Course" +msgstr "Активный курс" + +#: .\utils\unfold_translations.py:51 +msgid "Popular Courses" +msgstr "Популярные курсы" + +#: .\utils\unfold_translations.py:57 +msgid "Success:" +msgstr "Успешно:" + +#: .\utils\unfold_translations.py:58 +msgid "Pending:" +msgstr "В ожидании:" + +#: .\utils\unfold_translations.py:59 +msgid "Waiting:" +msgstr "Ожидание" + +#: .\utils\unfold_translations.py:60 +msgid "Failed:" +msgstr "Ошибка:" + +#: .\utils\unfold_translations.py:61 +msgid "Choose file to upload" +msgstr "Выберите файл для загрузки" + +#: .\utils\unfold_translations.py:62 +msgid "Date from" +msgstr "Дата с" + +#: .\utils\unfold_translations.py:63 +msgid "Date to" +msgstr "Дата по" + +#: .\utils\unfold_translations.py:64 .\utils\unfold_translations.py:90 +msgid "By Date Joined" +msgstr "По дате регистрации" + +#: .\utils\unfold_translations.py:65 +msgid "Type to search" +msgstr "Введите для поиска" + +#: .\utils\unfold_translations.py:67 +msgid "Apply filters" +msgstr "Применить фильтры" + +#: .\utils\unfold_translations.py:68 +msgid "From" +msgstr "От" + +#: .\utils\unfold_translations.py:69 +msgid "To" +msgstr "До" + +#: .\utils\unfold_translations.py:70 +msgid "video link" +msgstr "ссылка на видео" + +#: .\utils\unfold_translations.py:73 +msgid "Delete" +msgstr "Удалить" + +#: .\utils\unfold_translations.py:75 +msgid "Add Weekly Timing" +msgstr "Добавить еженедельное расписание" + +#: .\utils\unfold_translations.py:76 +msgid "Delete All" +msgstr "Удалить все" + +#: .\utils\unfold_translations.py:77 +msgid "Add Course Features" +msgstr "Добавить особенности курса" + +#: .\utils\unfold_translations.py:78 +msgid "Is Staff" +msgstr "Статус персонала" + +#: .\utils\unfold_translations.py:79 +msgid "Deleted at" +msgstr "Удалено в" + +#: .\utils\unfold_translations.py:81 +msgid "Type of the user" +msgstr "Тип пользователя" + +#: .\utils\unfold_translations.py:82 +#, python-brace-format +msgid "" +"Raw passwords are not stored, so there is no way to see this user’s " +"password, but you can change the password using this form." +msgstr "Исходные пароли не хранятся, поэтому нет возможности увидеть пароль этого пользователя, но вы можете изменить пароль, используя эту форму." + +#: .\utils\unfold_translations.py:87 +msgid "Users:" +msgstr "Пользователи:" + +#: .\utils\unfold_translations.py:88 +msgid "users :" +msgstr "пользователи :" + +#: .\utils\unfold_translations.py:89 +msgid "By" +msgstr "По" + +#: .\utils\unfold_translations.py:91 +msgid "( both fields are showing )" +msgstr "( показаны оба поля )" + +#: .\utils\unfold_translations.py:92 +msgid "(both fields are showing)" +msgstr "(показаны оба поля)" + +#: .\utils\unfold_translations.py:93 +msgid "Imam Javad Admin" +msgstr "Админ Имам Джавад" + +#: .\utils\unfold_translations.py:94 +msgid "Imam Javad School" +msgstr "Школа Имама Джавада" + +#: .\utils\unfold_translations.py:95 +msgid "Dovoodi Admin" +msgstr "Админ Довуди" + +#: .\utils\unfold_translations.py:96 +msgid "Dovodbi Application" +msgstr "Приложение Довудби" + +#: .\utils\unfold_translations.py:97 +msgid "Updated Today" +msgstr "Обновлено сегодня" + +#: .\utils\unfold_translations.py:98 +msgid "Requires Action" +msgstr "Требуется действие" + +#: .\apps\account\admin\user.py:471 +msgid "{count} permission" +msgid_plural "{count} permissions" +msgstr[0] "{count} разрешение" +msgstr[1] "{count} разрешения" +msgstr[2] "{count} разрешений" +msgstr[3] "{count} разрешений" + +#: .\apps\account\admin\student.py:59 +msgid "{count} course" +msgid_plural "{count} courses" +msgstr[0] "{count} курс" +msgstr[1] "{count} курса" +msgstr[2] "{count} курсов" +msgstr[3] "{count} курсов" + +#: .\apps\account\admin\user.py:307 .\apps\account\admin\user.py:447 +msgid "{total} course" +msgid_plural "{total} courses" +msgstr[0] "{total} курс" +msgstr[1] "{total} курса" +msgstr[2] "{total} курсов" +msgstr[3] "{total} курсов"