<<<<<<< HEAD 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 rest_framework.authtoken.models import TokenProxy from ajaxdatatable.admin import AjaxDatatable from apps.account.models import User, Notification from django import forms from django.contrib import admin from django.urls import path, reverse from django.shortcuts import render, redirect from django.contrib import messages from apps.account.models import ClientUser, AdminUser @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',] @admin.register(User) class UserAdmin(UserAdmin, AjaxDatatable): list_display = ( 'device_id', 'email', 'fullname', 'user_type','last_login', 'device_os', 'date_joined', ) ordering = '-id', readonly_fields = ('date_joined',) exclude = ('password', 'user_permissions') add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'password1', 'password2'), }), ) search_fields = ( 'email', 'fullname', 'username', ) fieldsets = ( (_('Personal info'), {'fields': ('fullname', 'email', 'phone_number', 'avatar',)}), (_('Permissions'), { 'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'password'), }), (_('Important dates'), {'fields': ('last_login', 'date_joined', 'fcm')}), ) def save_model(self, request, obj, form, change): if not change: obj.set_password(form.cleaned_data['password1']) # obj.user_type = User.UserType.CLIENT super().save_model(request, obj, form, change) @admin.display(description='Phone Number') def _phone_number(self, obj): return obj.phone_number @admin.register(AdminUser) class AdminUserAdmin(UserAdmin, AjaxDatatable): list_display = ( 'email', 'fullname', 'user_type','last_login', 'date_joined', ) ordering = 'last_login', readonly_fields = ('date_joined',) exclude = ('password', 'user_permissions') add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': ('email', 'password1', 'password2'), }), ) search_fields = ( 'email', 'fullname', 'username', ) fieldsets = ( (_('Personal info'), {'fields': ('fullname', 'email', 'phone_number', 'avatar',)}), (_('Permissions'), { 'fields': ('is_active', 'is_staff', 'is_superuser', 'groups', 'password'), }), (_('Important dates'), {'fields': ('last_login', 'date_joined', 'fcm')}), ) def save_model(self, request, obj, form, change): if not change: obj.set_password(form.cleaned_data['password1']) # obj.user_type = User.UserType.CLIENT super().save_model(request, obj, form, change) @admin.display(description='Phone Number') def _phone_number(self, obj): return obj.phone_number admin.site.unregister(TokenProxy) ======= from django import forms from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin from django.contrib.auth.forms import UserChangeForm, UserCreationForm 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.templatetags.static import static from rest_framework.authtoken.models import TokenProxy from unfold.admin import ModelAdmin, StackedInline from unfold.contrib.forms.widgets import WysiwygWidget from unfold.decorators import display from unfold.forms import AdminPasswordChangeForm from unfold.sections import TableSection from unfold.contrib.filters.admin import RangeDateTimeFilter # Import Models from apps.account.models import User, ClientUser, StudentUser, ProfessorUser, LocationHistory from apps.course.models import Participant # Import Admin Sites from utils from utils.admin import project_admin_site, dovoodi_admin_site from apps.account.admin.location import LocationHistoryInline # ========================================================= # 1. Base User Admin (Logic Shared by all User types) # ========================================================= class UserAdmin(BaseUserAdmin, ModelAdmin): form = UserChangeForm add_form = UserCreationForm change_password_form = AdminPasswordChangeForm compressed_fields = False list_before_template = "account/user_list_section.html" list_display = ('fullname', 'email', 'is_active', 'display_date_joined',) ordering = ("-id",) search_fields = ('email', 'fullname', 'username',) list_filter = [ "is_active", "is_staff", ("last_login", RangeDateTimeFilter), ("date_joined", RangeDateTimeFilter), ] inlines = [LocationHistoryInline] add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': (('fullname', 'email'), 'phone_number', 'birthdate', 'gender', 'avatar', 'skill', 'info'), }), (_('Location'), { 'fields': ('city', 'country'), 'classes': ('collapse',), }), (_('Password'), { 'fields': ('password1', 'password2'), 'classes': ('collapse',), }), (_('Permissions'), { 'fields': ('is_active', 'is_staff', 'is_superuser', 'groups'), 'classes': ('collapse',), }), ) fieldsets = ( (None, {"fields": ("email", "fullname")}), (_("Basic Information"), { "fields": ("gender", "avatar", "phone_number", "birthdate", 'info', 'skill', "password"), "classes": ["tab"], }), (_('Country & City'), { 'fields': ('city', 'country'), "classes": ["tab"], }), (_('Device Information'), { 'fields': ('device_id', 'device_os', 'fcm', 'language',), "classes": ["tab"], }), (_('Authentication'), { 'fields': ('display_auth_token',), "classes": ["tab"], }), (_('Permissions'), { 'fields': ('user_type', 'is_active', 'is_staff', 'groups'), "classes": ["tab"], }), (_('Important dates'), { 'fields': ('last_login', 'date_joined', 'deleted_at'), "classes": ["tab"], }), ) formfield_overrides = { models.TextField: {"widget": WysiwygWidget} } radio_fields = {"gender": admin.HORIZONTAL} readonly_fields = ["last_login", "date_joined", "display_auth_token"] @display(description=_("Date Joined")) def display_date_joined(self, instance: User): return instance.date_joined.strftime("%Y-%m-%d %H:%M") if instance.date_joined else "-" @display(description=_("Last Login")) def display_last_login(self, instance: User): return instance.last_login.strftime("%Y-%m-%d %H:%M") if instance.last_login else "-" @display(description=_("Authentication Token")) def display_auth_token(self, instance: User): from rest_framework.authtoken.models import Token try: token, created = Token.objects.get_or_create(user=instance) return format_html('{}', token.key) except Exception as e: return format_html('{}', str(e)) def get_queryset(self, request): qs = super().get_queryset(request) return qs.filter(email__isnull=False) # ========================================================= # 2. Specific User Type Admins # ========================================================= class GuestUserAdmin(UserAdmin): list_display = ('device_id', 'device_os', 'is_active', 'display_date_joined',) def has_add_permission(self, request): if '_popup' in request.GET and request.GET['_popup'] == '1': return True return False def get_queryset(self, request): qs = super().get_queryset(request) return qs.filter(email__isnull=True) @display(description=_("Date Joined")) def display_date_joined(self, instance: User): return instance.date_joined.strftime("%Y-%m-%d %H:%M") if instance.date_joined else "-" class StudentParticipantInline(StackedInline): """Inline to show courses a student has joined""" model = Participant extra = 0 readonly_fields = ('course', 'joined_date', 'course_status', 'course_professor') fields = ('course', 'course_status', 'course_professor', 'joined_date', 'is_active') verbose_name = _('Course Participation') verbose_name_plural = _('Course Participations') autocomplete_fields = ['course'] tab = True def get_queryset(self, request): qs = super().get_queryset(request) return qs.select_related('course', 'course__professor') @admin.display(description=_('Course Status')) def course_status(self, obj): if obj.course: return obj.course.get_status_display() return '-' @admin.display(description=_('Professor')) def course_professor(self, obj): if obj.course and obj.course.professor: return obj.course.professor.fullname or obj.course.professor.email return '-' def has_add_permission(self, request, obj=None): return True def has_change_permission(self, request, obj=None): return True def has_delete_permission(self, request, obj=None): return True class StudentUserAdmin(UserAdmin): list_display = ('display_header', 'email', 'gender', 'display_age', 'courses_count') add_fieldsets = ( (None, { 'classes': ('wide',), 'fields': (('fullname', 'email'), 'phone_number', 'avatar', 'birthdate', 'gender'), }), (_('Location'), { 'fields': (('city', 'country'),), 'classes': ('collapse',), }), (_('password'), { 'fields': ('password1', 'password2',), 'classes': ('collapse',), }), ) inlines = [StudentParticipantInline, LocationHistoryInline] @display(description=_("Student"), header=True) def display_header(self, instance: StudentUser): avatar_path = instance.avatar.url if instance.avatar else static("images/reading(1).png") return [ instance.fullname, None, None, { "path": avatar_path, "height": 30, "width": 36, "borderless": True, }, ] @display(description=_("Age")) def display_age(self, instance: StudentUser): from datetime import date if not instance.birthdate: return "-" today = date.today() 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) @display(description=_("Courses"), dropdown=True) def courses_count(self, instance: StudentUser): total = instance.participated_courses.count() items = [] for participant in instance.participated_courses.all(): course = participant.course title = format_html( """
{} visibility
""", course.title, course.id ) items.append({"title": title}) if total == 0: return "-" return { "title": f"{total} {_('courses')}", "items": items, "striped": True, } def get_queryset(self, request): return super().get_queryset(request).prefetch_related( "participated_courses", "participated_courses__course", ) class CourseTableSection(TableSection): verbose_name = _("Course Categories") related_name = "courses" height = 380 fields = ["title", "status", "edit_link"] def edit_link(self, instance): return format_html( '' 'visibility' '', instance.id ) edit_link.short_description = _("Edit") class ProfessorUserAdmin(UserAdmin): list_display = ('display_header', 'email', 'courses_count') list_sections = [CourseTableSection] save_as = True @display(description=_("Professor"), header=True) def display_header(self, instance: StudentUser): avatar_path = instance.avatar.url if instance.avatar else static("images/reading(1).png") return [ instance.fullname, None, None, { "path": avatar_path, "height": 30, "width": 50, "borderless": True, "squared": True, }, ] @display(description=_("Courses"), dropdown=True) def courses_count(self, instance: ProfessorUser): total = instance.courses.count() items = [] for course in instance.courses.all(): title = format_html( """
{} visibility
""", course.title, course.id ) items.append({"title": title}) if total == 0: return "-" return { "title": f"{total} {_('courses')}", "items": items, "striped": True, } def get_queryset(self, request): return super().get_queryset(request).prefetch_related("courses") class GroupAdmin(BaseGroupAdmin, ModelAdmin): list_display = ('name', 'permissions_count') search_fields = ('name',) ordering = ('name',) filter_horizontal = ('permissions',) fieldsets = ( (None, {'fields': ('name',)}), (_('Permissions'), {'fields': ('permissions',), 'classes': ['tab']}), ) @display(description=_("Permissions")) def permissions_count(self, obj): count = obj.permissions.count() return f"{count} {_('permissions')}" if count > 0 else "-" # ========================================================= # 3. Registrations (SAFE METHOD) # ========================================================= # A. DEFAULT DJANGO ADMIN (SAFE REGISTRATION) # This is required because plugins like 'django-filer' expect User to be registered here. try: admin.site.unregister(User) except admin.sites.NotRegistered: pass try: admin.site.register(User, UserAdmin) except admin.sites.AlreadyRegistered: pass # B. PROJECT ADMIN SITE (Imam Javad) project_admin_site.register(User, UserAdmin) project_admin_site.register(ClientUser, GuestUserAdmin) project_admin_site.register(StudentUser, StudentUserAdmin) project_admin_site.register(ProfessorUser, ProfessorUserAdmin) project_admin_site.register(Group, GroupAdmin) # C. DOVOODI ADMIN SITE dovoodi_admin_site.register(User, UserAdmin) dovoodi_admin_site.register(ClientUser, GuestUserAdmin) dovoodi_admin_site.register(Group, GroupAdmin) # D. Unregister TokenProxy safely (Cleaner UI) try: admin.site.unregister(TokenProxy) except admin.sites.NotRegistered: pass >>>>>>> develop