Files
Student-Management-System-I…/app/admin.py
T
2025-04-20 17:46:30 +05:45

1081 lines
37 KiB
Python

# Standard library imports
import json
from datetime import datetime
# Core Django imports
from django.contrib import admin
from django.contrib.auth.admin import GroupAdmin, UserAdmin
from django.contrib.auth.models import Group, User
from django.db import transaction
from django.db.models import Q
from django.forms import ModelForm
from django.http import JsonResponse
from django.urls import path
from django.utils.translation import gettext_lazy as _
# Local app imports
from app.firebase import FCMDevice
from app.forms import ParentAdminForm, StaffAdminForm, StudentAdminForm
from app.models import (
Attendance,
AttendanceRecord,
Batch,
Course,
CourseTracking,
Institute,
InstituteFeedback,
Notice,
Parent,
ParentFeedback,
ParentInstituteFeedback,
ResetToken,
Routine,
Staff,
StaffInstituteFeedback,
StaffLeave,
Student,
StudentFeedback,
StudentLeave,
Subject,
SubjectFile,
TeacherParentMeeting,
TOTPSecret,
)
# Register your models here.
class CustomAdminSite(admin.AdminSite):
def get_app_list(self, request, app_label=None):
# Original app list
app_list = super().get_app_list(request, app_label)
model_map = {
# Authentication Models
"User": {
"group": "Authentication",
"group_label": "authentication",
"display_name": "Users",
},
"Group": {
"group": "Authentication",
"group_label": "authentication",
"display_name": "Groups",
},
"TOTPSecret": {
"group": "Authentication",
"group_label": "authentication",
"display_name": "TOTP Keys",
},
"ResetToken": {
"group": "Authentication",
"group_label": "authentication",
"display_name": "Reset Tokens",
},
# Organization Models
"Institute": {
"group": "Organization",
"group_label": "organization",
"display_name": "Institute",
},
"Batch": {
"group": "Organization",
"group_label": "organization",
"display_name": "Batches",
},
"Notice": {
"group": "Organization",
"group_label": "organization",
"display_name": "Notices",
},
# Course Management Models
"Course": {
"group": "Course Management",
"group_label": "course_management",
"display_name": "Courses",
},
"Subject": {
"group": "Course Management",
"group_label": "course_management",
"display_name": "Subjects",
},
"SubjectFile": {
"group": "Course Management",
"group_label": "course_management",
"display_name": "Materials",
},
"CourseTracking": {
"group": "Course Management",
"group_label": "course_management",
"display_name": "Progress",
},
# Human Management Models
"Parent": {
"group": "Human Management",
"group_label": "human_management",
"display_name": "Parents",
},
"Student": {
"group": "Human Management",
"group_label": "human_management",
"display_name": "Students",
},
"Staff": {
"group": "Human Management",
"group_label": "human_management",
"display_name": "Staff",
},
# Schedule Management
"Routine": {
"group": "Schedule Management",
"group_label": "schedule_management",
"display_name": "Classes",
},
"TeacherParentMeeting": {
"group": "Schedule Management",
"group_label": "schedule_management",
"display_name": "PT Meetings",
},
# Leave Management
"StaffLeave": {
"group": "Leave Management",
"group_label": "leave_management",
"display_name": "Staff Leave",
},
"StudentLeave": {
"group": "Leave Management",
"group_label": "leave_management",
"display_name": "Student Leave",
},
# Attendance Models
"Attendance": {
"group": "Attendance",
"group_label": "attendance",
"display_name": "Attendance",
},
"AttendanceRecord": {
"group": "Attendance",
"group_label": "attendance",
"display_name": "Records",
},
# Feedback Models
"StudentFeedback": {
"group": "Feedback",
"group_label": "feedback",
"display_name": "Student Feedback",
},
"InstituteFeedback": {
"group": "Feedback",
"group_label": "feedback",
"display_name": "Student-Institute",
},
"StaffInstituteFeedback": {
"group": "Feedback",
"group_label": "feedback",
"display_name": "Staff-Institute",
},
"ParentFeedback": {
"group": "Feedback",
"group_label": "feedback",
"display_name": "Parent Feedback",
},
"ParentInstituteFeedback": {
"group": "Feedback",
"group_label": "feedback",
"display_name": "Parent-Institute",
},
# Device Management
"FCMDevice": {
"group": "Device Management",
"group_label": "device_management",
"display_name": "Devices",
},
}
# If app_label is specified, return only that app
if app_label:
for app in app_list:
if app["app_label"] == app_label:
return [app]
return []
grouped_app_list = [{"name": "Main", "app_label": "dashboard", "models": []}]
grouped_models = {}
for app in app_list:
for model in app["models"]:
model_name = model["object_name"]
if model_name in model_map:
group = model_map[model_name]["group"]
if group not in grouped_models:
grouped_models[group] = {
"name": group,
"app_label": model_map[model_name]["group_label"],
"models": [],
}
# Apply custom display name if available
if "display_name" in model_map[model_name]:
model["name"] = model_map[model_name]["display_name"]
grouped_models[group]["models"].append(model)
dashboard_item = {
"name": "Dashboard",
"app_label": "dashboard",
"models": [
{
"name": "Dashboard",
"object_name": "Dashboard",
"perms": {"has_module_perms": True},
"admin_url": "/app/dashboard",
"view_only": True,
}
],
}
grouped_app_list = [dashboard_item] + list(grouped_models.values())
return grouped_app_list
# Create a custom admin site instance
custom_admin_site = CustomAdminSite(name="custom_admin")
@admin.register(Institute, site=custom_admin_site)
class InstituteModel(admin.ModelAdmin):
list_display = ("id", "name", "address", "phone")
search_fields = ("name", "phone")
list_filter = ("name", "phone")
advanced_filter_fields = ("name", "phone")
@admin.register(Course, site=custom_admin_site)
class CourseModel(admin.ModelAdmin):
list_display = (
"id",
"name",
"duration",
"duration_type",
"get_total_duration",
"is_active",
)
search_fields = ("name", "duration", "duration_type")
list_filter = ("duration_type", "duration", "is_active")
advanced_filter_fields = ("name", "duration", "duration_type", "is_active")
def get_total_duration(self, obj):
if obj.duration_type == "Semester":
return f"{obj.duration} years ({obj.duration * 2} semesters)"
return f"{obj.duration} year{'s' if obj.duration > 1 else ''}"
get_total_duration.short_description = "Total Duration"
fieldsets = (
(None, {"fields": ("name",)}),
(
"Duration Settings",
{
"fields": ("duration", "duration_type"),
"description": "For yearly courses, duration is in years. For semester-based courses, duration is in years (2 semesters per year).",
},
),
)
@admin.register(Batch, site=custom_admin_site)
class BatchAdmin(admin.ModelAdmin):
list_display = ("name", "year", "is_active")
search_fields = ("name", "year")
list_filter = ("name", "year", "is_active")
advanced_filter_fields = ("name", "year", "is_active")
@admin.register(Subject, site=custom_admin_site)
class SubjectModel(admin.ModelAdmin):
list_display = (
"id",
"name",
"course",
"get_period_display",
"has_syllabus",
"file_count",
)
search_fields = ("name", "course__name")
list_filter = ("course", "period_or_year")
advanced_filter_fields = ("name", "course", "period_or_year")
def has_syllabus(self, obj):
return bool(obj.syllabus_pdf)
has_syllabus.boolean = True
has_syllabus.short_description = "Has Syllabus"
def file_count(self, obj):
return obj.subjectfile_set.count()
file_count.short_description = "Additional Files"
def get_period_display(self, obj):
if obj.course.duration_type == "Semester":
return f"Semester {obj.period_or_year}"
return f"Year {obj.period_or_year}"
get_period_display.short_description = "Period"
def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context["show_save_and_add_another"] = True
extra_context["show_save"] = False
return super().add_view(request, form_url, extra_context)
def response_add(self, request, obj, post_url_continue=None):
if "_addanother" in request.POST:
# Pre-fill the course and period fields for the next entry
return self.response_post_save_add(request, obj)
return super().response_add(request, obj, post_url_continue)
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
if obj and obj.course:
if obj.course.duration_type == "Semester":
form.base_fields["period_or_year"].label = "Semester"
form.base_fields[
"period_or_year"
].help_text = f"Enter semester number (1-{obj.course.duration * 2})"
else:
form.base_fields["period_or_year"].label = "Year"
form.base_fields[
"period_or_year"
].help_text = f"Enter year number (1-{obj.course.duration})"
# Keep the last used course selected for next entry
if not obj and request.method == "GET":
if "course" in request.GET:
form.base_fields["course"].initial = request.GET["course"]
if "period_or_year" in request.GET:
form.base_fields["period_or_year"].initial = request.GET[
"period_or_year"
]
return form
def response_post_save_add(self, request, obj):
from django.http import HttpResponseRedirect
from django.urls import reverse
opts = self.model._meta
url = reverse(f"admin:{opts.app_label}_{opts.model_name}_add")
return HttpResponseRedirect(
f"{url}?course={obj.course.id}&period_or_year={obj.period_or_year}"
)
@admin.register(Student, site=custom_admin_site)
class StudentAdmin(admin.ModelAdmin):
form = StudentAdminForm
list_display = ("id", "name", "phone", "course", "status", "fcm_token")
search_fields = ("name", "phone", "course__name", "fcm_token")
list_filter = ("course", "status", "gender", "joining_date")
advanced_filter_fields = ("name", "phone", "course", "batch", "status", "fcm_token")
def has_view_permission(self, request, obj=None):
"""Check if user has view permission"""
return request.user.has_perm("app.view_student")
def has_add_permission(self, request):
"""Check if user has add permission"""
return request.user.has_perm("app.add_student")
def has_change_permission(self, request, obj=None):
"""Check if user has change permission"""
return request.user.has_perm("app.change_student")
def has_delete_permission(self, request, obj=None):
"""Check if user has delete permission"""
return request.user.has_perm("app.delete_student")
def has_module_permission(self, request):
"""Check if user has any permissions for this app"""
return request.user.has_module_perms("app")
def get_readonly_fields(self, request, obj=None):
if obj:
return self.readonly_fields + ("password",)
return self.readonly_fields
@admin.register(Staff, site=custom_admin_site)
class StaffAdmin(admin.ModelAdmin):
form = StaffAdminForm
list_display = ("id", "name", "phone", "designation", "joining_date", "fcm_token")
search_fields = ("name", "phone", "designation", "fcm_token")
list_filter = ("designation", "joining_date", "gender")
advanced_filter_fields = ("name", "phone", "designation", "fcm_token")
readonly_fields = ("password",)
ordering = ("name",)
def save_model(self, request, obj, form, change):
"""Just save the model, group assignment is handled in the form"""
super().save_model(request, obj, form, change)
def has_view_permission(self, request, obj=None):
"""Check if user has view permission"""
return request.user.has_perm("app.view_staff")
def has_add_permission(self, request):
"""Check if user has add permission"""
return request.user.has_perm("app.add_staff")
def has_change_permission(self, request, obj=None):
"""Check if user has change permission"""
return request.user.has_perm("app.change_staff")
def has_delete_permission(self, request, obj=None):
"""Check if user has delete permission"""
return request.user.has_perm("app.delete_staff")
def has_module_permission(self, request):
"""Check if user has any permissions for this app"""
return request.user.has_module_perms("app")
def get_readonly_fields(self, request, obj=None):
if obj:
return self.readonly_fields + ("password",)
return self.readonly_fields
class AttendanceRecordForm(ModelForm):
def clean(self):
cleaned_data = super().clean()
if not cleaned_data.get("student"):
if self.instance and self.instance.pk:
cleaned_data["student"] = self.instance.student
return cleaned_data
class Meta:
model = AttendanceRecord
fields = "__all__"
class AttendanceRecordInline(admin.TabularInline):
model = AttendanceRecord
form = AttendanceRecordForm
extra = 0
can_delete = False # Prevent deletion
def get_formset(self, request, obj=None, **kwargs):
formset = super().get_formset(request, obj, **kwargs)
return formset
@admin.register(Attendance, site=custom_admin_site)
class AttendanceAdmin(admin.ModelAdmin):
list_display = ["date", "routine", "class_status", "teacher", "teacher_attend"]
inlines = [AttendanceRecordInline]
search_fields = ("date", "routine__name", "teacher__name")
list_filter = ("date", "routine", "teacher")
advanced_filter_fields = ("date", "routine", "teacher")
def save_model(self, request, obj, form, change):
is_new = not obj.pk
super().save_model(request, obj, form, change)
if is_new and obj.routine:
students = Student.objects.filter(routines__routine=obj.routine).distinct()
for student in students:
AttendanceRecord.objects.create(
attendance=obj, student=student, student_attend=False
)
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
# Preserve existing records
for instance in instances:
if instance.pk and not instance.student_id:
original = AttendanceRecord.objects.get(pk=instance.pk)
instance.student = original.student
instance.save()
formset.save_m2m()
@admin.register(Notice, site=custom_admin_site)
class NoticeAdmin(admin.ModelAdmin):
list_display = ("title", "message", "created_at")
search_fields = ("title", "message")
list_filter = ("title", "message")
advanced_filter_fields = ("title", "message")
@admin.register(Routine, site=custom_admin_site)
class RoutineAdmin(admin.ModelAdmin):
list_display = [
"course",
"subject",
"teacher",
"start_time",
"end_time",
"period_or_year",
"is_active",
]
list_filter = ["is_active", "course", "subject", "teacher", "period_or_year"]
search_fields = ["course__name", "subject__name", "teacher__name"]
ordering = ["start_time"]
change_form_template = "admin/app/routine/change_form.html"
def get_urls(self):
urls = super().get_urls()
custom_urls = [
path(
"add-multiple/",
self.admin_site.admin_view(self.save_routines),
name="routine-add-multiple",
),
]
return custom_urls + urls
def save_routines(self, request):
if request.method == "POST":
try:
data = json.loads(request.body)
routines = data.get("routines", [])
if not routines:
return JsonResponse(
{"success": False, "message": "No routines data provided"}
)
with (
transaction.atomic()
): # Use transaction to ensure all routines are created or none
created_routines = []
for index, routine_data in enumerate(routines):
try:
# Validate required fields
required_fields = [
"course",
"subject",
"teacher",
"start_time",
"end_time",
"period_or_year",
]
missing_fields = [
field
for field in required_fields
if field not in routine_data
or not routine_data.get(field)
]
if missing_fields:
error_msg = f"Missing required fields: {', '.join(missing_fields)}"
raise ValueError(error_msg)
# Convert IDs to integers
try:
course_id = int(routine_data["course"])
subject_id = int(routine_data["subject"])
teacher_id = int(routine_data["teacher"])
period = int(routine_data["period_or_year"])
except (ValueError, TypeError) as e:
raise ValueError(f"Error parsing IDs: {str(e)}")
# Validate time format
try:
start_time = datetime.strptime(
routine_data["start_time"], "%H:%M"
).time()
end_time = datetime.strptime(
routine_data["end_time"], "%H:%M"
).time()
except ValueError as e:
raise ValueError(
"Invalid time format. Please use HH:MM format."
)
# Check for existing routine
existing_routine = Routine.objects.filter(
course_id=course_id,
subject_id=subject_id,
period_or_year=period,
start_time=start_time,
end_time=end_time,
).first()
if existing_routine:
# Update existing routine
existing_routine.teacher_id = teacher_id
existing_routine.save()
created_routines.append(existing_routine)
else:
# Create new routine
routine = Routine.objects.create(
course_id=course_id,
subject_id=subject_id,
teacher_id=teacher_id,
start_time=start_time,
end_time=end_time,
period_or_year=period,
is_active=True,
)
created_routines.append(routine)
except ValueError as e:
raise # Re-raise to trigger rollback
except Exception as e:
raise # Re-raise to trigger rollback
return JsonResponse(
{
"success": True,
"message": f"Successfully saved {len(created_routines)} routines",
"routines": [
{
"id": r.id,
"course": r.course.name,
"subject": r.subject.name,
"teacher": r.teacher.name,
"start_time": r.start_time.strftime("%H:%M"),
"end_time": r.end_time.strftime("%H:%M"),
"period_or_year": r.period_or_year,
}
for r in created_routines
],
}
)
except ValueError as e:
return JsonResponse({"success": False, "message": str(e)})
except json.JSONDecodeError as e:
return JsonResponse(
{"success": False, "message": f"Invalid JSON data: {str(e)}"}
)
except Exception as e:
return JsonResponse({"success": False, "message": str(e)})
return JsonResponse({"success": False, "message": "Invalid request method"})
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "subject":
if request.GET.get("course"):
kwargs["queryset"] = Subject.objects.filter(
course_id=request.GET.get("course"),
period_or_year=request.GET.get("period_or_year", 1),
)
else:
kwargs["queryset"] = Subject.objects.none()
elif db_field.name == "teacher":
kwargs["queryset"] = Staff.objects.filter(
Q(designation__icontains="Teacher")
| Q(designation__icontains="Staff")
| Q(designation__icontains="Principal")
| Q(designation__isnull=True)
| Q(designation="")
).order_by("name")
return super().formfield_for_foreignkey(db_field, request, **kwargs)
def get_form(self, request, obj=None, **kwargs):
form = super().get_form(request, obj, **kwargs)
if not obj: # Only for adding new routines
form.base_fields["course"].widget.attrs["class"] = "select2"
form.base_fields["subject"].widget.attrs["class"] = "select2"
form.base_fields["teacher"].widget.attrs["class"] = "select2"
form.base_fields["period_or_year"].initial = 1
return form
def add_view(self, request, form_url="", extra_context=None):
extra_context = extra_context or {}
extra_context["show_save_and_add_another"] = True
return super().add_view(request, form_url, extra_context)
class Media:
css = {
"all": (
"https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css",
)
}
js = ("https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js",)
@admin.register(StaffLeave, site=custom_admin_site)
class StaffLeaveAdmin(admin.ModelAdmin):
list_display = (
"staff",
"start_date",
"end_date",
"get_status_display",
"created_at",
)
list_filter = ("status", "start_date", "end_date", "created_at")
search_fields = ("staff__name", "message")
readonly_fields = ("created_at", "updated_at")
ordering = ("-created_at",)
raw_id_fields = ("staff",)
@admin.register(StudentLeave, site=custom_admin_site)
class StudentLeaveAdmin(admin.ModelAdmin):
list_display = (
"student",
"start_date",
"end_date",
"get_status_display",
"created_at",
)
list_filter = ("status", "start_date", "end_date", "created_at")
search_fields = ("student__name", "message")
readonly_fields = ("created_at", "updated_at")
ordering = ("-created_at",)
raw_id_fields = ("student",)
@admin.register(StudentFeedback, site=custom_admin_site)
class StudentFeedbackAdmin(admin.ModelAdmin):
list_display = ("student", "rating", "created_at")
list_filter = ("rating", "created_at")
search_fields = ("student__name", "feedback_text")
readonly_fields = ("created_at", "updated_at")
ordering = ("-created_at",)
@admin.register(CourseTracking, site=custom_admin_site)
class CourseTrackingAdmin(admin.ModelAdmin):
list_display = (
"student",
"course",
"progress_status",
"completion_percentage",
"start_date",
"expected_end_date",
)
list_filter = ("progress_status", "course", "start_date", "expected_end_date")
search_fields = ("student__name", "course__name", "notes")
readonly_fields = ("enrollment_date", "created_at", "updated_at")
ordering = ("-created_at",)
raw_id_fields = ("student", "course")
list_per_page = 25
def has_view_permission(self, request, obj=None):
return request.user.has_perm("app.view_coursetracking")
def has_add_permission(self, request):
return request.user.has_perm("app.add_coursetracking")
def has_change_permission(self, request, obj=None):
return request.user.has_perm("app.change_coursetracking")
def has_delete_permission(self, request, obj=None):
return request.user.has_perm("app.delete_coursetracking")
def has_module_permission(self, request):
return request.user.has_module_perms("app")
@admin.register(InstituteFeedback, site=custom_admin_site)
class InstituteFeedbackAdmin(admin.ModelAdmin):
list_display = (
"display_name",
"institute",
"feedback_type",
"rating",
"is_anonymous",
"is_public",
"created_at",
)
list_filter = (
"feedback_type",
"rating",
"is_anonymous",
"is_public",
"created_at",
"institute",
)
search_fields = ("user__name", "institute__name", "feedback_text")
readonly_fields = ("created_at", "updated_at")
date_hierarchy = "created_at"
def display_name(self, obj):
return obj.display_name
display_name.short_description = "Student Name"
fieldsets = (
(None, {"fields": ("institute", "user")}),
(
"Feedback Details",
{
"fields": (
"feedback_type",
"rating",
"feedback_text",
"is_anonymous",
"is_public",
)
},
),
(
"Metadata",
{"fields": ("created_at", "updated_at"), "classes": ("collapse",)},
),
)
def get_queryset(self, request):
return super().get_queryset(request).select_related("user", "institute")
class Media:
css = {"all": ("admin/css/forms.css",)}
js = ("admin/js/collapse.js",)
@admin.register(StaffInstituteFeedback, site=custom_admin_site)
class StaffInstituteFeedbackAdmin(admin.ModelAdmin):
list_display = (
"display_name",
"institute",
"feedback_type",
"rating",
"is_anonymous",
"is_public",
"created_at",
)
list_filter = (
"feedback_type",
"rating",
"is_anonymous",
"is_public",
"created_at",
"institute",
)
search_fields = ("staff__name", "institute__name", "feedback_text")
readonly_fields = ("created_at", "updated_at")
date_hierarchy = "created_at"
def display_name(self, obj):
return obj.display_name
display_name.short_description = "Staff Name"
fieldsets = (
(None, {"fields": ("institute", "staff")}),
(
"Feedback Details",
{
"fields": (
"feedback_type",
"rating",
"feedback_text",
"is_anonymous",
"is_public",
)
},
),
(
"Metadata",
{"fields": ("created_at", "updated_at"), "classes": ("collapse",)},
),
)
def get_queryset(self, request):
return super().get_queryset(request).select_related("staff", "institute")
@admin.register(FCMDevice, site=custom_admin_site)
class FCMDeviceAdmin(admin.ModelAdmin):
list_display = (
"id",
"token",
"user_type",
"is_active",
"created_at",
"last_active",
)
search_fields = ("token",)
list_filter = ("user_type", "is_active", "created_at", "last_active")
readonly_fields = ("created_at", "last_active")
ordering = ("-last_active",)
fieldsets = (
(None, {"fields": ("token", "user_type", "is_active", "is_fallback")}),
(
"Metadata",
{"fields": ("created_at", "last_active"), "classes": ("collapse",)},
),
)
@admin.register(SubjectFile, site=custom_admin_site)
class SubjectFileAdmin(admin.ModelAdmin):
list_display = ("id", "title", "subject", "uploaded_by", "uploaded_at")
search_fields = ("title", "subject__name", "uploaded_by__name")
list_filter = ("subject__course", "uploaded_at")
autocomplete_fields = ["subject", "uploaded_by"]
@admin.register(Parent, site=custom_admin_site)
class ParentAdmin(admin.ModelAdmin):
form = ParentAdminForm
list_display = ("id", "name", "phone", "email", "fcm_token")
search_fields = ("name", "phone", "email", "fcm_token")
list_filter = ("students",)
advanced_filter_fields = ("name", "phone", "email", "students", "fcm_token")
def has_view_permission(self, request, obj=None):
"""Check if user has view permission"""
return request.user.has_perm("app.view_parent")
def has_add_permission(self, request):
"""Check if user has add permission"""
return request.user.has_perm("app.add_parent")
def has_change_permission(self, request, obj=None):
"""Check if user has change permission"""
return request.user.has_perm("app.change_parent")
def has_delete_permission(self, request, obj=None):
"""Check if user has delete permission"""
return request.user.has_perm("app.delete_parent")
def has_module_permission(self, request):
"""Check if user has any permissions for this app"""
return request.user.has_module_perms("app")
def get_readonly_fields(self, request, obj=None):
if obj:
return self.readonly_fields + ("password",)
return self.readonly_fields
@admin.register(ParentFeedback, site=custom_admin_site)
class ParentFeedbackAdmin(admin.ModelAdmin):
list_display = ("parent", "teacher", "student", "rating", "created_at")
list_filter = ("rating", "created_at", "teacher", "student")
search_fields = ("parent__name", "teacher__name", "student__name", "feedback_text")
readonly_fields = ("created_at", "updated_at")
ordering = ("-created_at",)
raw_id_fields = ("parent", "teacher", "student")
def get_queryset(self, request):
return (
super().get_queryset(request).select_related("parent", "teacher", "student")
)
@admin.register(ParentInstituteFeedback, site=custom_admin_site)
class ParentInstituteFeedbackAdmin(admin.ModelAdmin):
list_display = (
"parent",
"institute",
"feedback_type",
"rating",
"is_anonymous",
"created_at",
)
list_filter = ("feedback_type", "rating", "is_anonymous", "created_at", "institute")
search_fields = ("parent__name", "institute__name", "feedback_text")
readonly_fields = ("created_at", "updated_at")
ordering = ("-created_at",)
raw_id_fields = ("parent", "institute")
def get_queryset(self, request):
return super().get_queryset(request).select_related("parent", "institute")
@admin.register(TeacherParentMeeting, site=custom_admin_site)
class TeacherParentMeetingAdmin(admin.ModelAdmin):
list_display = (
"meeting_date",
"meeting_time",
"duration",
"status",
"is_online",
"meeting_link",
)
list_filter = (
"status",
"is_online",
"meeting_date",
)
search_fields = (
"agenda",
"notes",
"cancellation_reason",
)
readonly_fields = ("created_at", "updated_at")
ordering = ("-meeting_date", "-meeting_time")
fieldsets = (
(
"Meeting Details",
{
"fields": (
"meeting_date",
"meeting_time",
"duration",
"status",
"is_online",
"meeting_link",
)
},
),
(
"Meeting Content",
{
"fields": ("agenda", "notes"),
"classes": ("collapse",),
},
),
(
"Cancellation Details",
{
"fields": ("cancellation_reason",),
"classes": ("collapse",),
},
),
(
"Metadata",
{
"fields": (
"created_at",
"updated_at",
),
"classes": ("collapse",),
},
),
)
@admin.register(TOTPSecret, site=custom_admin_site)
class TOTPSecretAdmin(admin.ModelAdmin):
list_display = ("identifier", "created_at", "expires_at", "is_expired")
search_fields = ("identifier",)
readonly_fields = ("created_at", "expires_at")
ordering = ("-created_at",)
def is_expired(self, obj):
return obj.is_expired()
is_expired.boolean = True
is_expired.short_description = "Expired"
@admin.register(ResetToken, site=custom_admin_site)
class ResetTokenAdmin(admin.ModelAdmin):
list_display = ("identifier", "token", "created_at", "expires_at", "is_expired")
search_fields = ("identifier", "token")
readonly_fields = ("created_at", "expires_at")
ordering = ("-created_at",)
def is_expired(self, obj):
return obj.is_expired()
is_expired.boolean = True
is_expired.short_description = "Expired"
custom_admin_site.register(User, UserAdmin)
custom_admin_site.register(Group, GroupAdmin)