uchill/backend/apps/users/admin.py

270 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
Административная панель для пользователей.
"""
from django import forms
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin as BaseUserAdmin
from django.contrib.auth.forms import UserChangeForm as BaseUserChangeForm, UserCreationForm as BaseUserCreationForm
from django.utils.translation import gettext_lazy as _
from .models import User, Client, Parent, Mentor, MentorStudentConnection
from .utils import normalize_phone
class UserChangeForm(BaseUserChangeForm):
class Meta(BaseUserChangeForm.Meta):
model = User
def clean_phone(self):
value = self.cleaned_data.get('phone', '') or ''
return normalize_phone(value) if value else ''
class UserCreationForm(BaseUserCreationForm):
class Meta(BaseUserCreationForm.Meta):
model = User
def clean_phone(self):
value = self.cleaned_data.get('phone', '') or ''
return normalize_phone(value) if value else ''
class ClientMentorInline(admin.TabularInline):
"""Инлайн для связи ментор — студент (на странице ментора)."""
model = Client.mentors.through
fk_name = 'user'
extra = 0
verbose_name = _('Студент')
verbose_name_plural = _('Связь ментор — студент')
autocomplete_fields = ['client']
@admin.register(User)
class UserAdmin(BaseUserAdmin):
"""Административная панель для модели User."""
form = UserChangeForm
add_form = UserCreationForm
list_display = [
'email', 'first_name', 'last_name', 'role',
'is_active', 'email_verified', 'created_at'
]
list_filter = [
'role', 'is_active', 'is_staff', 'is_superuser',
'email_verified', 'created_at'
]
search_fields = ['email', 'first_name', 'last_name', 'phone', 'telegram_username']
ordering = ['-created_at']
fieldsets = (
(None, {
'fields': ('email', 'password')
}),
(_('Персональная информация'), {
'fields': (
'first_name', 'last_name', 'birth_date',
'avatar', 'bio', 'phone'
)
}),
(_('Роль и права'), {
'fields': (
'role', 'is_active', 'is_staff',
'is_superuser', 'groups', 'user_permissions'
)
}),
(_('Telegram'), {
'fields': ('telegram_id', 'telegram_username')
}),
(_('Верификация'), {
'fields': ('email_verified', 'email_verification_token')
}),
(_('Настройки'), {
'fields': (
'timezone', 'language',
'notifications_enabled', 'email_notifications',
'telegram_notifications'
)
}),
(_('Блокировка'), {
'fields': ('is_blocked', 'blocked_reason', 'blocked_at'),
'classes': ('collapse',)
}),
(_('Важные даты'), {
'fields': ('last_login', 'last_activity', 'date_joined', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': (
'email', 'password1', 'password2', 'first_name',
'last_name', 'role', 'is_staff', 'is_active'
),
}),
)
readonly_fields = ['created_at', 'updated_at', 'last_login', 'date_joined']
def get_readonly_fields(self, request, obj=None):
"""Сделать некоторые поля только для чтения после создания."""
# Email теперь можно редактировать в админке
return self.readonly_fields
@admin.register(Mentor)
class MentorAdmin(BaseUserAdmin):
"""Отдельная админка только для менторов."""
form = UserChangeForm
add_form = UserCreationForm
list_display = [
'email', 'first_name', 'last_name',
'clients_display', 'is_active', 'universal_code', 'created_at'
]
list_filter = ['is_active', 'email_verified', 'created_at']
search_fields = ['email', 'first_name', 'last_name', 'phone', 'universal_code']
ordering = ['-created_at']
inlines = [ClientMentorInline]
fieldsets = (
(None, {'fields': ('email', 'password')}),
(_('Персональная информация'), {
'fields': ('first_name', 'last_name', 'birth_date', 'avatar', 'bio', 'phone')
}),
(_('Роль и права'), {
'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'user_permissions')
}),
(_('Telegram'), {'fields': ('telegram_id', 'telegram_username')}),
(_('Верификация'), {'fields': ('email_verified', 'email_verification_token')}),
(_('Настройки'), {
'fields': ('timezone', 'language', 'universal_code',
'notifications_enabled', 'email_notifications', 'telegram_notifications',
'ai_trust_draft', 'ai_trust_publish')
}),
(_('Важные даты'), {
'fields': ('last_login', 'last_activity', 'date_joined', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
add_fieldsets = (
(None, {
'classes': ('wide',),
'fields': ('email', 'password1', 'password2', 'first_name', 'last_name', 'is_staff', 'is_active'),
}),
)
readonly_fields = ['universal_code', 'created_at', 'updated_at', 'last_login', 'date_joined']
@admin.display(description=_('Студенты'))
def clients_display(self, obj):
clients = obj.clients.all()[:5]
if not clients:
return ''
names = [c.user.get_full_name() or c.user.email for c in clients]
extra = obj.clients.count() - 5
if extra > 0:
names.append(f'+{extra}')
return ', '.join(names)
def save_model(self, request, obj, form, change):
obj.role = 'mentor'
super().save_model(request, obj, form, change)
@admin.register(Client)
class ClientAdmin(admin.ModelAdmin):
"""Административная панель для модели Client."""
list_display = [
'user', 'mentors_display', 'grade', 'school', 'total_lessons',
'completed_lessons', 'enrollment_date'
]
list_filter = ['enrollment_date', 'created_at']
search_fields = [
'user__email', 'user__first_name',
'user__last_name', 'school', 'grade'
]
filter_horizontal = ['mentors']
readonly_fields = ['enrollment_date', 'created_at', 'updated_at']
fieldsets = (
(_('Пользователь'), {
'fields': ('user',)
}),
(_('Учебная информация'), {
'fields': ('grade', 'school', 'learning_goals')
}),
(_('Менторы'), {
'fields': ('mentors',)
}),
(_('Статистика'), {
'fields': ('total_lessons', 'completed_lessons')
}),
(_('Даты'), {
'fields': ('enrollment_date', 'created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
@admin.display(description=_('Менторы'))
def mentors_display(self, obj):
mentors = obj.mentors.all()[:5]
if not mentors:
return ''
names = [m.get_full_name() or m.email for m in mentors]
extra = obj.mentors.count() - 5
if extra > 0:
names.append(f'+{extra}')
return ', '.join(names)
@admin.register(Parent)
class ParentAdmin(admin.ModelAdmin):
"""Административная панель для модели Parent."""
list_display = [
'user', 'relation_type', 'can_view_progress',
'can_view_schedule', 'created_at'
]
list_filter = [
'relation_type', 'can_view_progress',
'can_view_schedule', 'can_receive_reports', 'created_at'
]
search_fields = [
'user__email', 'user__first_name', 'user__last_name'
]
filter_horizontal = ['children']
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
(_('Пользователь'), {
'fields': ('user',)
}),
(_('Информация о родителе'), {
'fields': ('relation_type',)
}),
(_('Дети'), {
'fields': ('children',)
}),
(_('Права доступа'), {
'fields': (
'can_view_progress', 'can_view_schedule',
'can_receive_reports'
)
}),
(_('Даты'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
@admin.register(MentorStudentConnection)
class MentorStudentConnectionAdmin(admin.ModelAdmin):
list_display = ['mentor', 'student', 'status', 'initiator', 'created_at']
list_filter = ['status', 'initiator', 'created_at']
search_fields = ['mentor__email', 'student__email', 'mentor__first_name', 'student__first_name']
readonly_fields = ['confirm_token', 'student_confirmed_at', 'parent_confirmed_at', 'created_at', 'updated_at']