uchill/backend/apps/schedule/admin.py

294 lines
10 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.contrib import admin
from django.utils.html import format_html
from django.utils.translation import gettext_lazy as _
from .models import Lesson, LessonTemplate, TimeSlot, Availability, Subject, MentorSubject
@admin.register(Lesson)
class LessonAdmin(admin.ModelAdmin):
"""Административная панель для занятий."""
list_display = [
'title', 'mentor', 'client', 'start_time',
'duration', 'status_badge', 'created_at'
]
list_filter = [
'status', 'start_time', 'mentor', 'subject', 'mentor_subject', 'created_at'
]
search_fields = [
'title', 'description', 'subject__name', 'mentor_subject__name', 'subject_name',
'mentor__email', 'mentor__first_name', 'mentor__last_name',
'client__user__email', 'client__user__first_name', 'client__user__last_name'
]
date_hierarchy = 'start_time'
readonly_fields = [
'end_time', 'created_at', 'updated_at',
'cancelled_at'
]
fieldsets = (
(_('Участники'), {
'fields': ('mentor', 'client')
}),
(_('Время'), {
'fields': ('start_time', 'duration', 'end_time')
}),
(_('Информация'), {
'fields': ('title', 'description', 'subject', 'mentor_subject', 'subject_name', 'template')
}),
(_('Статус'), {
'fields': ('status',)
}),
(_('Отмена'), {
'fields': (
'cancelled_by', 'cancellation_reason', 'cancelled_at'
),
'classes': ('collapse',)
}),
(_('Перенос'), {
'fields': ('rescheduled_from',),
'classes': ('collapse',)
}),
(_('Дополнительно'), {
'fields': (
'meeting_url', 'mentor_notes',
'reminder_sent'
),
'classes': ('collapse',)
}),
(_('Даты'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def status_badge(self, obj):
"""Отображение статуса с цветом."""
colors = {
'scheduled': '#3B82F6', # синий
'in_progress': '#10B981', # зеленый
'completed': '#6B7280', # серый
'cancelled': '#EF4444', # красный
'rescheduled': '#F59E0B', # оранжевый
}
color = colors.get(obj.status, '#6B7280')
return format_html(
'<span style="background-color: {}; color: white; padding: 3px 10px; border-radius: 3px;">{}</span>',
color,
obj.get_status_display()
)
status_badge.short_description = 'Статус'
def get_queryset(self, request):
"""Оптимизация запросов."""
return super().get_queryset(request).select_related(
'mentor', 'client', 'client__user', 'template',
'cancelled_by', 'rescheduled_from', 'subject', 'mentor_subject'
)
@admin.register(LessonTemplate)
class LessonTemplateAdmin(admin.ModelAdmin):
"""Административная панель для шаблонов занятий."""
list_display = [
'title', 'mentor', 'subject', 'duration',
'is_active', 'lessons_count', 'created_at'
]
list_filter = ['is_active', 'subject', 'mentor_subject', 'created_at']
search_fields = [
'title', 'description', 'subject__name', 'mentor_subject__name', 'subject_name',
'mentor__email', 'mentor__first_name', 'mentor__last_name'
]
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
(_('Владелец'), {
'fields': ('mentor',)
}),
(_('Информация'), {
'fields': ('title', 'description', 'subject', 'mentor_subject', 'subject_name', 'duration')
}),
(_('Настройки'), {
'fields': ('is_active', 'meeting_url', 'color')
}),
(_('Даты'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def lessons_count(self, obj):
"""Количество занятий созданных из шаблона."""
return obj.lessons.count()
lessons_count.short_description = 'Занятий создано'
def get_queryset(self, request):
"""Оптимизация запросов."""
return super().get_queryset(request).select_related('mentor', 'subject', 'mentor_subject')
@admin.register(TimeSlot)
class TimeSlotAdmin(admin.ModelAdmin):
"""Административная панель для временных слотов."""
list_display = [
'mentor', 'start_time', 'end_time',
'availability_badge', 'is_recurring', 'created_at'
]
list_filter = [
'is_available', 'is_booked', 'is_recurring',
'start_time', 'created_at'
]
search_fields = [
'mentor__email', 'mentor__first_name', 'mentor__last_name'
]
date_hierarchy = 'start_time'
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
(_('Ментор'), {
'fields': ('mentor',)
}),
(_('Время'), {
'fields': ('start_time', 'end_time')
}),
(_('Статус'), {
'fields': ('is_available', 'is_booked', 'lesson')
}),
(_('Повторение'), {
'fields': ('is_recurring', 'recurring_day')
}),
(_('Даты'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def availability_badge(self, obj):
"""Отображение доступности."""
if obj.is_booked:
return format_html(
'<span style="background-color: #EF4444; color: white; padding: 3px 10px; border-radius: 3px;">Забронирован</span>'
)
elif obj.is_available:
return format_html(
'<span style="background-color: #10B981; color: white; padding: 3px 10px; border-radius: 3px;">Доступен</span>'
)
else:
return format_html(
'<span style="background-color: #6B7280; color: white; padding: 3px 10px; border-radius: 3px;">Недоступен</span>'
)
availability_badge.short_description = 'Доступность'
def get_queryset(self, request):
"""Оптимизация запросов."""
return super().get_queryset(request).select_related('mentor', 'lesson')
@admin.register(Availability)
class AvailabilityAdmin(admin.ModelAdmin):
"""Административная панель для доступности."""
list_display = [
'mentor', 'day_display', 'time_range',
'is_recurring', 'is_active', 'created_at'
]
list_filter = [
'is_active', 'is_recurring', 'day_of_week', 'created_at'
]
search_fields = [
'mentor__email', 'mentor__first_name', 'mentor__last_name', 'notes'
]
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
(_('Ментор'), {
'fields': ('mentor',)
}),
(_('Время'), {
'fields': ('day_of_week', 'specific_date', 'start_time', 'end_time')
}),
(_('Тип'), {
'fields': ('is_recurring', 'is_active')
}),
(_('Исключения'), {
'fields': ('exception_dates', 'notes'),
'classes': ('collapse',)
}),
(_('Даты'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def day_display(self, obj):
"""Отображение дня."""
if obj.is_recurring and obj.day_of_week is not None:
return obj.get_day_of_week_display()
elif obj.specific_date:
return obj.specific_date.strftime('%d.%m.%Y')
return '-'
day_display.short_description = 'День'
def time_range(self, obj):
"""Отображение временного диапазона."""
return f"{obj.start_time.strftime('%H:%M')} - {obj.end_time.strftime('%H:%M')}"
time_range.short_description = 'Время'
def get_queryset(self, request):
"""Оптимизация запросов."""
return super().get_queryset(request).select_related('mentor')
@admin.register(Subject)
class SubjectAdmin(admin.ModelAdmin):
"""Административная панель для предметов."""
list_display = ['name', 'is_active', 'lessons_count', 'created_at']
list_filter = ['is_active', 'created_at']
search_fields = ['name']
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
(_('Информация'), {
'fields': ('name', 'is_active')
}),
(_('Даты'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def lessons_count(self, obj):
"""Количество занятий с этим предметом."""
return obj.lessons.count()
lessons_count.short_description = 'Занятий'
@admin.register(MentorSubject)
class MentorSubjectAdmin(admin.ModelAdmin):
"""Административная панель для кастомных предметов менторов."""
list_display = ['name', 'mentor', 'usage_count', 'created_at']
list_filter = ['created_at']
search_fields = ['name', 'mentor__email', 'mentor__first_name', 'mentor__last_name']
readonly_fields = ['created_at', 'updated_at']
fieldsets = (
(_('Информация'), {
'fields': ('mentor', 'name', 'usage_count')
}),
(_('Даты'), {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',)
}),
)
def get_queryset(self, request):
"""Оптимизация запросов."""
return super().get_queryset(request).select_related('mentor')